summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/sound')
-rw-r--r--src/devices/sound/315-5641.cpp2
-rw-r--r--src/devices/sound/ad1848.cpp5
-rw-r--r--src/devices/sound/aica.cpp23
-rw-r--r--src/devices/sound/ap2010pcm.cpp227
-rw-r--r--src/devices/sound/ap2010pcm.h65
-rw-r--r--src/devices/sound/asc.cpp98
-rw-r--r--src/devices/sound/astrocde.cpp18
-rw-r--r--src/devices/sound/astrocde.h5
-rw-r--r--src/devices/sound/awacs.cpp13
-rw-r--r--src/devices/sound/ay8910.cpp634
-rw-r--r--src/devices/sound/beep.cpp6
-rw-r--r--src/devices/sound/beep.h14
-rw-r--r--src/devices/sound/bsmt2000.cpp14
-rw-r--r--src/devices/sound/bsmt2000.h4
-rw-r--r--src/devices/sound/c140.cpp137
-rw-r--r--src/devices/sound/c140.h4
-rw-r--r--src/devices/sound/c352.cpp5
-rw-r--r--src/devices/sound/c352.h2
-rw-r--r--src/devices/sound/cdda.cpp44
-rw-r--r--src/devices/sound/cdda.h18
-rw-r--r--src/devices/sound/cdp1864.cpp46
-rw-r--r--src/devices/sound/cdp1869.cpp31
-rw-r--r--src/devices/sound/cem3394.cpp28
-rw-r--r--src/devices/sound/cf61909.cpp161
-rw-r--r--src/devices/sound/cf61909.h63
-rw-r--r--src/devices/sound/dac.h8
-rw-r--r--src/devices/sound/dac3350a.cpp332
-rw-r--r--src/devices/sound/dac3350a.h46
-rw-r--r--src/devices/sound/dac76.h20
-rw-r--r--src/devices/sound/dave.cpp9
-rw-r--r--src/devices/sound/dave.h4
-rw-r--r--src/devices/sound/discrete.cpp135
-rw-r--r--src/devices/sound/discrete.h2
-rw-r--r--src/devices/sound/dspv.cpp86
-rw-r--r--src/devices/sound/dspv.h25
-rw-r--r--src/devices/sound/dspvd.cpp6
-rw-r--r--src/devices/sound/es1373.cpp102
-rw-r--r--src/devices/sound/es1373.h5
-rw-r--r--src/devices/sound/es5503.cpp123
-rw-r--r--src/devices/sound/es5503.h2
-rw-r--r--src/devices/sound/es5506.cpp396
-rw-r--r--src/devices/sound/es8712.cpp56
-rw-r--r--src/devices/sound/es8712.h2
-rw-r--r--src/devices/sound/esqpump.cpp2
-rw-r--r--src/devices/sound/flt_biquad.cpp71
-rw-r--r--src/devices/sound/flt_biquad.h10
-rw-r--r--src/devices/sound/flt_vol.cpp13
-rw-r--r--src/devices/sound/flt_vol.h3
-rw-r--r--src/devices/sound/gaelco.cpp20
-rw-r--r--src/devices/sound/gaelco.h2
-rw-r--r--src/devices/sound/gew.cpp641
-rw-r--r--src/devices/sound/gew.h160
-rw-r--r--src/devices/sound/gew7.cpp206
-rw-r--r--src/devices/sound/gew7.h30
-rw-r--r--src/devices/sound/hc55516.cpp41
-rw-r--r--src/devices/sound/hc55516.h95
-rw-r--r--src/devices/sound/huc6230.cpp6
-rw-r--r--src/devices/sound/i5000.h1
-rw-r--r--src/devices/sound/ics2115.cpp20
-rw-r--r--src/devices/sound/iremga20.cpp5
-rw-r--r--src/devices/sound/iremga20.h2
-rw-r--r--src/devices/sound/k005289.cpp10
-rw-r--r--src/devices/sound/k005289.h12
-rw-r--r--src/devices/sound/k007232.cpp2
-rw-r--r--src/devices/sound/k051649.cpp95
-rw-r--r--src/devices/sound/k051649.h22
-rw-r--r--src/devices/sound/k053260.cpp75
-rw-r--r--src/devices/sound/k053260.h29
-rw-r--r--src/devices/sound/k054539.cpp10
-rw-r--r--src/devices/sound/k054539.h4
-rw-r--r--src/devices/sound/k056800.cpp6
-rw-r--r--src/devices/sound/ks0164.cpp140
-rw-r--r--src/devices/sound/ks0164.h6
-rw-r--r--src/devices/sound/l7a1045_l6028_dsp_a.cpp59
-rw-r--r--src/devices/sound/l7a1045_l6028_dsp_a.h3
-rw-r--r--src/devices/sound/lc7535.cpp11
-rw-r--r--src/devices/sound/lc7535.h6
-rw-r--r--src/devices/sound/lc78836m.cpp208
-rw-r--r--src/devices/sound/lc78836m.h66
-rw-r--r--src/devices/sound/lc82310.cpp301
-rw-r--r--src/devices/sound/lc82310.h92
-rw-r--r--src/devices/sound/lmc1992.cpp6
-rw-r--r--src/devices/sound/lmc1992.h6
-rw-r--r--src/devices/sound/lynx.cpp7
-rw-r--r--src/devices/sound/mas3507d.cpp133
-rw-r--r--src/devices/sound/mas3507d.h17
-rw-r--r--src/devices/sound/mea8000.cpp2
-rw-r--r--src/devices/sound/meg.cpp180
-rw-r--r--src/devices/sound/meg.h71
-rw-r--r--src/devices/sound/megd.cpp118
-rw-r--r--src/devices/sound/mixer.cpp2
-rw-r--r--src/devices/sound/mixer.h3
-rw-r--r--src/devices/sound/mm5837.cpp3
-rw-r--r--src/devices/sound/mos6560.cpp30
-rw-r--r--src/devices/sound/mos6581.cpp8
-rw-r--r--src/devices/sound/mos7360.cpp22
-rw-r--r--src/devices/sound/mp3_audio.cpp90
-rw-r--r--src/devices/sound/mp3_audio.h37
-rw-r--r--src/devices/sound/msm5205.cpp5
-rw-r--r--src/devices/sound/msm5232.cpp150
-rw-r--r--src/devices/sound/msm5232.h43
-rw-r--r--src/devices/sound/multipcm.cpp662
-rw-r--r--src/devices/sound/multipcm.h139
-rw-r--r--src/devices/sound/namco_163.cpp2
-rw-r--r--src/devices/sound/namco_163.h2
-rw-r--r--src/devices/sound/nes_apu.cpp140
-rw-r--r--src/devices/sound/nes_apu.h32
-rw-r--r--src/devices/sound/nes_apu_vt.cpp2
-rw-r--r--src/devices/sound/nes_defs.h14
-rw-r--r--src/devices/sound/nn71003f.cpp84
-rw-r--r--src/devices/sound/nn71003f.h42
-rw-r--r--src/devices/sound/okiadpcm.cpp5
-rw-r--r--src/devices/sound/okim6258.cpp76
-rw-r--r--src/devices/sound/okim6258.h16
-rw-r--r--src/devices/sound/okim6295.cpp33
-rw-r--r--src/devices/sound/okim6295.h2
-rw-r--r--src/devices/sound/okim6376.cpp84
-rw-r--r--src/devices/sound/okim6376.h12
-rw-r--r--src/devices/sound/okim6588.cpp232
-rw-r--r--src/devices/sound/okim6588.h88
-rw-r--r--src/devices/sound/okim9810.cpp5
-rw-r--r--src/devices/sound/okim9810.h2
-rw-r--r--src/devices/sound/pokey.cpp488
-rw-r--r--src/devices/sound/pokey.h44
-rw-r--r--src/devices/sound/qs1000.cpp68
-rw-r--r--src/devices/sound/qs1000.h9
-rw-r--r--src/devices/sound/qsound.cpp12
-rw-r--r--src/devices/sound/qsound.h4
-rw-r--r--src/devices/sound/qsoundhle.cpp5
-rw-r--r--src/devices/sound/qsoundhle.h2
-rw-r--r--src/devices/sound/rf5c400.cpp2
-rw-r--r--src/devices/sound/rf5c400.h2
-rw-r--r--src/devices/sound/rolandpcm.cpp20
-rw-r--r--src/devices/sound/rolandpcm.h10
-rw-r--r--src/devices/sound/s14001a.cpp24
-rw-r--r--src/devices/sound/samples.cpp24
-rw-r--r--src/devices/sound/samples.h43
-rw-r--r--src/devices/sound/scsp.cpp20
-rw-r--r--src/devices/sound/scsp.h2
-rw-r--r--src/devices/sound/segapcm.cpp5
-rw-r--r--src/devices/sound/segapcm.h2
-rw-r--r--src/devices/sound/setapcm.cpp5
-rw-r--r--src/devices/sound/setapcm.h2
-rw-r--r--src/devices/sound/sn76477.cpp85
-rw-r--r--src/devices/sound/sn76477.h16
-rw-r--r--src/devices/sound/sn76496.cpp14
-rw-r--r--src/devices/sound/sn76496.h2
-rw-r--r--src/devices/sound/sp0250.cpp10
-rw-r--r--src/devices/sound/sp0250.h5
-rw-r--r--src/devices/sound/sp0256.cpp113
-rw-r--r--src/devices/sound/sp0256.h41
-rw-r--r--src/devices/sound/spkrdev.cpp2
-rw-r--r--src/devices/sound/spu.cpp26
-rw-r--r--src/devices/sound/spu.h52
-rw-r--r--src/devices/sound/spu_tables.cpp838
-rw-r--r--src/devices/sound/spureverb.cpp2
-rw-r--r--src/devices/sound/ssi263hle.cpp230
-rw-r--r--src/devices/sound/ssi263hle.h76
-rw-r--r--src/devices/sound/stt_sa1.cpp190
-rw-r--r--src/devices/sound/stt_sa1.h57
-rw-r--r--src/devices/sound/swp00.cpp1828
-rw-r--r--src/devices/sound/swp00.h215
-rw-r--r--src/devices/sound/swp20.cpp190
-rw-r--r--src/devices/sound/swp20.h42
-rw-r--r--src/devices/sound/swp30.cpp1469
-rw-r--r--src/devices/sound/swp30.h200
-rw-r--r--src/devices/sound/swp30d.cpp172
-rw-r--r--src/devices/sound/swp30d.h (renamed from src/devices/sound/megd.h)16
-rw-r--r--src/devices/sound/swx00.cpp711
-rw-r--r--src/devices/sound/swx00.h124
-rw-r--r--src/devices/sound/t6721a.cpp26
-rw-r--r--src/devices/sound/t6721a.h4
-rw-r--r--src/devices/sound/ta7630.cpp27
-rw-r--r--src/devices/sound/ta7630.h13
-rw-r--r--src/devices/sound/tms5110.cpp30
-rw-r--r--src/devices/sound/tms5220.cpp92
-rw-r--r--src/devices/sound/tms5220.h8
-rw-r--r--src/devices/sound/tt5665.cpp5
-rw-r--r--src/devices/sound/tt5665.h2
-rw-r--r--src/devices/sound/uda1344.cpp20
-rw-r--r--src/devices/sound/upd1771.cpp3
-rw-r--r--src/devices/sound/upd7752.cpp3
-rw-r--r--src/devices/sound/upd7759.cpp86
-rw-r--r--src/devices/sound/upd7759.h24
-rw-r--r--src/devices/sound/upd933.cpp636
-rw-r--r--src/devices/sound/upd933.h103
-rw-r--r--src/devices/sound/upd934g.cpp55
-rw-r--r--src/devices/sound/upd934g.h9
-rw-r--r--src/devices/sound/vgm_visualizer.cpp4
-rw-r--r--src/devices/sound/vlm5030.cpp268
-rw-r--r--src/devices/sound/vlm5030.h11
-rw-r--r--src/devices/sound/votrax.cpp77
-rw-r--r--src/devices/sound/votrax.h15
-rw-r--r--src/devices/sound/vrender0.cpp13
-rw-r--r--src/devices/sound/x1_010.cpp29
-rw-r--r--src/devices/sound/x1_010.h2
-rw-r--r--src/devices/sound/xt446.cpp38
-rw-r--r--src/devices/sound/xt446.h4
-rw-r--r--src/devices/sound/ym2154.cpp12
-rw-r--r--src/devices/sound/ymf271.cpp19
-rw-r--r--src/devices/sound/ymf271.h2
-rw-r--r--src/devices/sound/ymfm_mame.h36
-rw-r--r--src/devices/sound/ymopl.cpp8
-rw-r--r--src/devices/sound/ymopl.h4
-rw-r--r--src/devices/sound/ymopm.cpp2
-rw-r--r--src/devices/sound/ymopm.h2
-rw-r--r--src/devices/sound/ymopn.cpp4
-rw-r--r--src/devices/sound/ymopn.h2
-rw-r--r--src/devices/sound/ymz280b.cpp13
-rw-r--r--src/devices/sound/ymz280b.h2
-rw-r--r--src/devices/sound/zsg2.cpp6
211 files changed, 12281 insertions, 5075 deletions
diff --git a/src/devices/sound/315-5641.cpp b/src/devices/sound/315-5641.cpp
index 7b5f06b0d8b..defb3cbb9ed 100644
--- a/src/devices/sound/315-5641.cpp
+++ b/src/devices/sound/315-5641.cpp
@@ -18,8 +18,6 @@ sega_315_5641_pcm_device::sega_315_5641_pcm_device(const machine_config &mconfig
void sega_315_5641_pcm_device::device_start()
{
- m_fifocallback.resolve_safe();
-
upd7759_device::device_start();
save_item(NAME(m_fifo_data), 0x40);
diff --git a/src/devices/sound/ad1848.cpp b/src/devices/sound/ad1848.cpp
index 7c62b4912ef..b47e3c4a683 100644
--- a/src/devices/sound/ad1848.cpp
+++ b/src/devices/sound/ad1848.cpp
@@ -5,7 +5,7 @@
// TODO: Emulate pin-compatible Crystal Semiconductor CS4231 and its extra Mode 2 features
#include "emu.h"
-#include "sound/ad1848.h"
+#include "ad1848.h"
#include "speaker.h"
@@ -33,8 +33,7 @@ void ad1848_device::device_add_mconfig(machine_config &config)
void ad1848_device::device_start()
{
m_timer = timer_alloc(FUNC(ad1848_device::update_tick), this);
- m_irq_cb.resolve_safe();
- m_drq_cb.resolve_safe();
+
save_item(NAME(m_regs.idx));
save_item(NAME(m_addr));
save_item(NAME(m_stat));
diff --git a/src/devices/sound/aica.cpp b/src/devices/sound/aica.cpp
index a746f80eae0..bb6cc9de495 100644
--- a/src/devices/sound/aica.cpp
+++ b/src/devices/sound/aica.cpp
@@ -91,8 +91,8 @@ static constexpr s32 clip18(int x) { return std::clamp(x, -131072, 131071); }
#define DISDL(slot) ((slot->udata.data[0x24 / 2] >> 0x8) & 0x000F)
#define DIPAN(slot) (MONO() ? 0 : ((slot->udata.data[0x24 / 2] >> 0x0) & 0x001F))
-#define EFSDL(slot) ((m_EFSPAN[slot * 4] >> 8) & 0x000f)
-#define EFPAN(slot) (MONO() ? 0 : ((m_EFSPAN[slot * 4] >> 0) & 0x001f))
+#define EFSDL(slot) ((m_EFSPAN[slot] >> 8) & 0x000f)
+#define EFPAN(slot) (MONO() ? 0 : ((m_EFSPAN[slot] >> 0) & 0x001f))
//Unimplemented
#define Q(slot) ((slot->udata.data[0x28 / 2] >> 0x0) & 0x001F) // (0.75 × register value - 3)
@@ -662,7 +662,7 @@ void aica_device::UpdateReg(int reg)
case 0x90:
case 0x91:
- if (!m_irq_cb.isnull())
+ if (!m_irq_cb.isunset())
{
u32 time;
@@ -681,7 +681,7 @@ void aica_device::UpdateReg(int reg)
break;
case 0x94:
case 0x95:
- if (!m_irq_cb.isnull())
+ if (!m_irq_cb.isunset())
{
u32 time;
@@ -700,7 +700,7 @@ void aica_device::UpdateReg(int reg)
break;
case 0x98:
case 0x99:
- if (!m_irq_cb.isnull())
+ if (!m_irq_cb.isunset())
{
u32 time;
@@ -727,7 +727,7 @@ void aica_device::UpdateReg(int reg)
case 0xa4: //SCIRE
case 0xa5:
- if (!m_irq_cb.isnull())
+ if (!m_irq_cb.isunset())
{
m_udata.data[0xa0 / 2] &= ~m_udata.data[0xa4 / 2];
ResetInterrupts();
@@ -754,7 +754,7 @@ void aica_device::UpdateReg(int reg)
case 0xad:
case 0xb0:
case 0xb1:
- if (!m_irq_cb.isnull())
+ if (!m_irq_cb.isunset())
{
m_IrqTimA = DecodeSCI(SCITMA);
m_IrqTimBC = DecodeSCI(SCITMB);
@@ -874,7 +874,7 @@ void aica_device::w16(u32 addr,u16 val)
if (addr <= 0x2044)
{
// printf("%x to EFSxx slot %d (addr %x)\n", val, (addr - 0x2000)/4, addr & 0x7f);
- m_EFSPAN[addr & 0x7f] = val;
+ m_EFSPAN[(addr & 0x7f) >> 2] = val;
}
}
else if (addr < 0x3000)
@@ -976,7 +976,7 @@ u16 aica_device::r16(u32 addr)
{
if (addr <= 0x2044)
{
- v = m_EFSPAN[addr & 0x7f];
+ v = m_EFSPAN[(addr & 0x7f) >> 2];
}
else if (addr < 0x2800)
{
@@ -1300,6 +1300,7 @@ void aica_device::DoMasterSamples(std::vector<read_stream_view> const &inputs, w
}
bufl.put_int(s, smpl * m_LPANTABLE[MVOL() << 0xd], 32768 << SHIFT);
+ // TODO: diverges with SCSP, also wut?
bufr.put_int(s, smpr * m_LPANTABLE[MVOL() << 0xd], 32768 << SHIFT);
}
}
@@ -1403,10 +1404,6 @@ void aica_device::device_start()
// init the emulation
Init();
- // set up the IRQ callbacks
- m_irq_cb.resolve_safe();
- m_main_irq_cb.resolve_safe();
-
m_stream = stream_alloc(2, 2, (int)m_rate);
// save state
diff --git a/src/devices/sound/ap2010pcm.cpp b/src/devices/sound/ap2010pcm.cpp
new file mode 100644
index 00000000000..234264f52f9
--- /dev/null
+++ b/src/devices/sound/ap2010pcm.cpp
@@ -0,0 +1,227 @@
+// 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, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
+{
+ auto &buffer = outputs[0];
+ buffer.fill(0);
+
+ 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 < buffer.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++;
+ }
+
+ buffer.put_int(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, buffer.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++;
+}
diff --git a/src/devices/sound/ap2010pcm.h b/src/devices/sound/ap2010pcm.h
new file mode 100644
index 00000000000..5d142fe580e
--- /dev/null
+++ b/src/devices/sound/ap2010pcm.h
@@ -0,0 +1,65 @@
+// license:BSD-3-Clause
+// copyright-holders:QUFB
+/**********************************************************************
+
+ PCM audio functions of the AP2010 LSI
+
+ According to the Advanced Pico Beena's 2005-04-05 press release, it supports
+ CELP with sample rates 8kHz and 16kHz. It is used as output for OGG files,
+ which are decoded by the BIOS to signed 16-bit big endian PCM.
+
+**********************************************************************/
+
+#ifndef MAME_SOUND_AP2010PCM_H
+#define MAME_SOUND_AP2010PCM_H
+
+#pragma once
+
+class ap2010pcm_device : public device_t, public device_sound_interface
+{
+public:
+ static constexpr feature_type imperfect_features() { return feature::SOUND; }
+
+ ap2010pcm_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
+
+ uint32_t reg_r(offs_t offset);
+ void reg_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
+
+protected:
+ // device_t implementation
+ virtual void device_start() override;
+
+ // device_sound_interface implementation
+ virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
+
+private:
+ // FIXME: Games check this against 0x1ff, but samples are lost with that limit
+ static inline constexpr uint16_t FIFO_MAX_SIZE = 0x800;
+
+ uint16_t fifo_pop();
+ uint16_t fifo_fast_pop();
+ void fifo_push(uint16_t sample);
+ void fifo_fast_push(uint16_t sample);
+
+ std::unique_ptr<uint32_t[]> m_regs;
+
+ float m_volume;
+
+ uint32_t m_sample_rate;
+
+ uint16_t m_fifo_data[FIFO_MAX_SIZE];
+ uint16_t m_fifo_size;
+ uint16_t m_fifo_head;
+ uint16_t m_fifo_tail;
+
+ uint16_t m_fifo_fast_data[FIFO_MAX_SIZE];
+ uint16_t m_fifo_fast_size;
+ uint16_t m_fifo_fast_head;
+ uint16_t m_fifo_fast_tail;
+
+ sound_stream *m_stream;
+};
+
+DECLARE_DEVICE_TYPE(AP2010PCM, ap2010pcm_device)
+
+#endif // MAME_SOUND_AP2010PCM_H
diff --git a/src/devices/sound/asc.cpp b/src/devices/sound/asc.cpp
index 2216b38f328..231e6b2357f 100644
--- a/src/devices/sound/asc.cpp
+++ b/src/devices/sound/asc.cpp
@@ -33,11 +33,15 @@
0x828: WAVETABLE 3 PHASE
0x82C: WAVETABLE 3 INCREMENT
+ TODO: rewrite this, we know so much more now and the "chip variant type" pattern must die.
+
***************************************************************************/
#include "emu.h"
#include "asc.h"
+#include "multibyte.h"
+
// device type definition
DEFINE_DEVICE_TYPE(ASC, asc_device, "asc", "ASC")
@@ -83,8 +87,6 @@ void asc_device::device_start()
save_item(NAME(m_regs));
save_item(NAME(m_phase));
save_item(NAME(m_incr));
-
- write_irq.resolve_safe();
}
@@ -209,8 +211,10 @@ void asc_device::sound_stream_update(sound_stream &stream, std::vector<read_stre
}
break;
- default: // V8/Sonora/Eagle/etc
- if (m_fifo_cap_a < 0x1ff)
+ // Sonora sets the 1/2 full flag continuously, ASC/EASC only does it when it happens
+ case asc_type::SONORA:
+ case asc_type::ARDBEG:
+ if (m_fifo_cap_a <= 0x200)
{
m_regs[R_FIFOSTAT-0x800] |= 1; // fifo A less than half full
@@ -218,23 +222,39 @@ void asc_device::sound_stream_update(sound_stream &stream, std::vector<read_stre
{
m_regs[R_FIFOSTAT-0x800] |= 2; // fifo A empty
}
- write_irq(ASSERT_LINE);
+ if (!(m_regs[0xf09 - 0x800] & 1))
+ {
+ write_irq(ASSERT_LINE);
+ }
}
- if (m_chip_type == asc_type::SONORA)
+ if (m_fifo_cap_b <= 0x200)
{
- if (m_fifo_cap_b < 0x1ff)
- {
- m_regs[R_FIFOSTAT-0x800] |= 4; // fifo B less than half full
+ m_regs[R_FIFOSTAT-0x800] |= 4; // fifo B less than half full
- if (m_fifo_cap_b == 0) // fifo B fully empty
- {
- m_regs[R_FIFOSTAT-0x800] |= 8; // fifo B empty
- }
+ if (m_fifo_cap_b == 0) // fifo B fully empty
+ {
+ m_regs[R_FIFOSTAT-0x800] |= 8; // fifo B empty
+ }
+ if (!(m_regs[0xf29 - 0x800] & 1))
+ {
write_irq(ASSERT_LINE);
}
}
break;
+
+ default: // V8/Eagle/etc
+ if (m_fifo_cap_a < 0x1ff)
+ {
+ m_regs[R_FIFOSTAT-0x800] |= 1; // fifo A less than half full
+
+ if (m_fifo_cap_a == 0) // fifo A fully empty
+ {
+ m_regs[R_FIFOSTAT-0x800] |= 2; // fifo A empty
+ }
+ write_irq(ASSERT_LINE);
+ }
+ break;
}
outL.put_int(i, smpll, 32768 / 64);
@@ -390,33 +410,17 @@ uint8_t asc_device::read(offs_t offset)
// WT inc/phase registers - rebuild from "live" copies"
if ((offset >= 0x810) && (offset <= 0x82f))
{
- m_regs[0x11] = m_phase[0]>>16;
- m_regs[0x12] = m_phase[0]>>8;
- m_regs[0x13] = m_phase[0];
- m_regs[0x15] = m_incr[0]>>16;
- m_regs[0x16] = m_incr[0]>>8;
- m_regs[0x17] = m_incr[0];
-
- m_regs[0x19] = m_phase[1]>>16;
- m_regs[0x1a] = m_phase[1]>>8;
- m_regs[0x1b] = m_phase[1];
- m_regs[0x1d] = m_incr[1]>>16;
- m_regs[0x1e] = m_incr[1]>>8;
- m_regs[0x1f] = m_incr[1];
-
- m_regs[0x21] = m_phase[2]>>16;
- m_regs[0x22] = m_phase[2]>>8;
- m_regs[0x23] = m_phase[2];
- m_regs[0x25] = m_incr[2]>>16;
- m_regs[0x26] = m_incr[2]>>8;
- m_regs[0x27] = m_incr[2];
-
- m_regs[0x29] = m_phase[3]>>16;
- m_regs[0x2a] = m_phase[3]>>8;
- m_regs[0x2b] = m_phase[3];
- m_regs[0x2d] = m_incr[3]>>16;
- m_regs[0x2e] = m_incr[3]>>8;
- m_regs[0x2f] = m_incr[3];
+ put_u24be(&m_regs[0x11], m_phase[0]);
+ put_u24be(&m_regs[0x15], m_incr[0]);
+
+ put_u24be(&m_regs[0x19], m_phase[1]);
+ put_u24be(&m_regs[0x1d], m_incr[1]);
+
+ put_u24be(&m_regs[0x21], m_phase[2]);
+ put_u24be(&m_regs[0x25], m_incr[2]);
+
+ put_u24be(&m_regs[0x29], m_phase[3]);
+ put_u24be(&m_regs[0x2d], m_incr[3]);
}
if (offset >= 0x1000)
@@ -442,11 +446,16 @@ void asc_device::write(offs_t offset, uint8_t data)
m_fifo_a[m_fifo_a_wrptr++] = data;
m_fifo_cap_a++;
- if (m_fifo_cap_a == 0x3ff)
+ if (m_fifo_cap_a == 0x400)
{
m_regs[R_FIFOSTAT-0x800] |= 2; // fifo A full
}
+ if (m_fifo_cap_a > 0x200)
+ {
+ m_regs[R_FIFOSTAT-0x800] &= ~1;
+ }
+
m_fifo_a_wrptr &= 0x3ff;
}
else
@@ -461,11 +470,16 @@ void asc_device::write(offs_t offset, uint8_t data)
m_fifo_b[m_fifo_b_wrptr++] = data;
m_fifo_cap_b++;
- if (m_fifo_cap_b == 0x3ff)
+ if (m_fifo_cap_b == 0x400)
{
m_regs[R_FIFOSTAT-0x800] |= 8; // fifo B full
}
+ if (m_fifo_cap_b > 0x200)
+ {
+ m_regs[R_FIFOSTAT-0x800] &= ~4;
+ }
+
m_fifo_b_wrptr &= 0x3ff;
}
else
diff --git a/src/devices/sound/astrocde.cpp b/src/devices/sound/astrocde.cpp
index 91d0fdc46b9..e29b6f31617 100644
--- a/src/devices/sound/astrocde.cpp
+++ b/src/devices/sound/astrocde.cpp
@@ -83,9 +83,9 @@ astrocade_io_device::astrocade_io_device(const machine_config &mconfig, const ch
, m_b_state(0)
, m_c_count(0)
, m_c_state(0)
- , m_si_callback(*this)
+ , m_si_callback(*this, 0)
, m_so_callback(*this)
- , m_pots(*this)
+ , m_pots(*this, 0)
{
memset(m_reg, 0, sizeof(uint8_t)*8);
memset(m_bitswap, 0, sizeof(uint8_t)*256);
@@ -93,20 +93,6 @@ astrocade_io_device::astrocade_io_device(const machine_config &mconfig, const ch
//-------------------------------------------------
-// device_resolve_objects - resolve objects that
-// may be needed for other devices to set
-// initial conditions at start time
-//-------------------------------------------------
-
-void astrocade_io_device::device_resolve_objects()
-{
- m_si_callback.resolve_safe(0);
- m_so_callback.resolve_all_safe();
- m_pots.resolve_all_safe(0);
-}
-
-
-//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
diff --git a/src/devices/sound/astrocde.h b/src/devices/sound/astrocde.h
index 387ef7f9ca3..bd62cf96e37 100644
--- a/src/devices/sound/astrocde.h
+++ b/src/devices/sound/astrocde.h
@@ -52,12 +52,11 @@ public:
template <std::size_t Pot> auto pot_cb() { return m_pots[Pot].bind(); }
protected:
- // device-level overrides
- virtual void device_resolve_objects() override;
+ // device_t implementation
virtual void device_start() override;
virtual void device_reset() override;
- // sound stream update overrides
+ // device_sound_interface implementation
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
public:
diff --git a/src/devices/sound/awacs.cpp b/src/devices/sound/awacs.cpp
index 07ec04e4f14..236852c854b 100644
--- a/src/devices/sound/awacs.cpp
+++ b/src/devices/sound/awacs.cpp
@@ -32,9 +32,9 @@ awacs_device::awacs_device(const machine_config &mconfig, const char *tag, devic
, device_sound_interface(mconfig, *this)
, m_irq_out_cb(*this)
, m_irq_in_cb(*this)
- , m_output_cb(*this)
+ , m_output_cb(*this, 0)
, m_input_cb(*this)
- , m_input_port_cb(*this)
+ , m_input_port_cb(*this, 0)
, m_output_port_cb(*this)
, m_stream(nullptr)
{
@@ -51,15 +51,6 @@ void awacs_device::device_start()
m_last_sample = attotime::zero;
- m_irq_out_cb.resolve_safe();
- m_irq_in_cb.resolve_safe();
-
- m_output_cb.resolve_safe(0);
- m_input_cb.resolve_safe();
-
- m_input_port_cb.resolve_safe(0);
- m_output_port_cb.resolve_safe();
-
save_item(NAME(m_extend));
save_item(NAME(m_ext_command));
save_item(NAME(m_ext_address));
diff --git a/src/devices/sound/ay8910.cpp b/src/devices/sound/ay8910.cpp
index 1f0acab67d7..92e9fcc7df3 100644
--- a/src/devices/sound/ay8910.cpp
+++ b/src/devices/sound/ay8910.cpp
@@ -1,193 +1,73 @@
// license:BSD-3-Clause
// copyright-holders:Couriersud
/*
- * Couriersud, July 2014:
- *
- * This documents recent work on the AY8910. A YM2149 is now on it's way from
- * Hong Kong as well.
- *
- * TODO:
- *
- * - Create a true sound device nAY8910 driver.
- * - implement approach outlined below in this driver.
- *
- * For years I had a AY8910 in my drawer. Arduinos were around as well.
- * Using the approach documented in this blog post
- * http://www.986-studio.com/2014/05/18/another-ay-entry/#more-476
- * I measured the output voltages using a Extech 520.
- *
- * Measurement Setup
- *
- * Laptop <--> Arduino <---> AY8910
- *
- * AY8910 Registers:
- * 0x07: 3f
- * 0x08: RV
- * 0x09: RV
- * 0x0A: RV
- *
- * Output was measured on Analog Output B with a resistor RD to
- * ground.
- *
- * Measurement results:
- *
- * RD 983 9.830k 99.5k 1.001M open
- *
- * RV B B B B B
- * 0 0.0000 0.0000 0.0001 0.0011 0.0616
- * 1 0.0106 0.0998 0.6680 1.8150 2.7260
- * 2 0.0150 0.1377 0.8320 1.9890 2.8120
- * 3 0.0222 0.1960 1.0260 2.1740 2.9000
- * 4 0.0320 0.2708 1.2320 2.3360 2.9760
- * 5 0.0466 0.3719 1.4530 2.4880 3.0440
- * 6 0.0665 0.4938 1.6680 2.6280 3.1130
- * 7 0.1039 0.6910 1.9500 2.7900 3.1860
- * 8 0.1237 0.7790 2.0500 2.8590 3.2340
- * 9 0.1986 1.0660 2.3320 3.0090 3.3090
- * 10 0.2803 1.3010 2.5050 3.0850 3.3380
- * 11 0.3548 1.4740 2.6170 3.1340 3.3590
- * 12 0.4702 1.6870 2.7340 3.1800 3.3730
- * 13 0.6030 1.8870 2.8410 3.2300 3.4050
- * 14 0.7530 2.0740 2.9280 3.2580 3.4170
- * 15 0.9250 2.2510 3.0040 3.2940 3.4380
- *
- * Using an equivalent model approach with two resistors
- *
- * 5V
- * |
- * Z
- * Z Resistor Value for RV
- * Z
- * |
- * +---> Output signal
- * |
- * Z
- * Z External RD
- * Z
- * |
- * GND
- *
- * will NOT work out of the box since RV = RV(RD).
- *
- * The following approach will be used going forward based on die pictures
- * of the AY8910 done by Dr. Stack van Hay:
- *
- *
- * 5V
- * _| D
- * G | NMOS
- * Vg ---|| Kn depends on volume selected
- * |_ S Vs
- * |
- * |
- * +---> VO Output signal
- * |
- * Z
- * Z External RD
- * Z
- * |
- * GND
- *
- * Whilst conducting, the FET operates in saturation mode:
- *
- * Id = Kn * (Vgs - Vth)^2
- *
- * Using Id = Vs / RD
- *
- * Vs = Kn * RD * (Vg - Vs - Vth)^2
- *
- * finally using Vg' = Vg - Vth
- *
- * Vs = Vg' + 1 / (2 * Kn * RD) - sqrt((Vg' + 1 / (2 * Kn * RD))^2 - Vg'^2)
- *
- * and finally
- *
- * VO = Vs
- *
- * and this can be used to re-Thenevin to 5V
- *
- * RVequiv = RD * ( 5V / VO - 1)
- *
- * The RV and Kn parameter are derived using least squares to match
- * calculation results with measurements.
- *
- * FIXME:
- * There is voltage of 60 mV measured with the EX520 (Ri ~ 10M). This may
- * be induced by cutoff currents from the 15 FETs.
- *
- */
-
-
-/***************************************************************************
-
- ay8910.cpp
-
- Emulation of the AY-3-8910 / YM2149 sound chip.
-
- Based on various code snippets by Ville Hallik, Michael Cuddy,
- Tatsuyuki Satoh, Fabrice Frances, Nicola Salmoria.
-
- Mostly rewritten by couriersud in 2008
-
- Public documentation:
-
- - http://privatfrickler.de/blick-auf-den-chip-soundchip-general-instruments-ay-3-8910/
- Die pictures of the AY8910
-
- - US Patent 4933980
-
- Games using ADSR: gyruss
-
- A list with more games using ADSR can be found here:
- http://mametesters.org/view.php?id=3043
-
- TODO:
- * Measure volume / envelope parameters for AY8930 expanded mode
- * YM2610 & YM2608 will need a separate flag in their config structures
- to distinguish between legacy and discrete mode.
-
- The rewrite also introduces a generic model for the DAC. This model is
- not perfect, but allows channel mixing based on a parametrized approach.
- This model also allows to factor in different loads on individual channels.
- If a better model is developed in the future or better measurements are
- available, the driver should be easy to change. The model is described
- later.
-
- In order to not break hundreds of existing drivers by default the flag
- AY8910_LEGACY_OUTPUT is used by drivers not changed to take into account the
- new model. All outputs are normalized to the old output range (i.e. 0 .. 7ffff).
- In the case of channel mixing, output range is 0...3 * 7fff.
-
- The main difference between the AY-3-8910 and the YM2149 is, that the
- AY-3-8910 datasheet mentions, that fixed volume level 0, which is set by
- registers 8 to 10 is "channel off". The YM2149 mentions, that the generated
- signal has a 2V DC component. This is confirmed by measurements. The approach
- taken here is to assume the 2V DC offset for all outputs for the YM2149.
- For the AY-3-8910, an offset is used if envelope is active for a channel.
- This is backed by oscilloscope pictures from the datasheet. If a fixed volume
- is set, i.e. envelope is disabled, the output voltage is set to 0V. Recordings
- I found on the web for gyruss indicate, that the AY-3-8910 offset should
- be around 0.2V. This will also make sound levels more compatible with
- user observations for scramble.
-
- The Model:
- 5V 5V
- | |
- / |
- Volume Level x >---| Z
- > Z Pullup Resistor RU
- | Z
- Z |
- Rx Z |
- Z |
- | |
- '-----+--------> >---+----> Output signal
- | |
- Z Z
- Pulldown RD Z Z Load RL
- Z Z
- | |
- GND GND
+
+Emulation of the AY-3-8910 / YM2149 sound chip.
+
+Based on various code snippets by Ville Hallik, Michael Cuddy,
+Tatsuyuki Satoh, Fabrice Frances, Nicola Salmoria.
+
+Mostly rewritten by couriersud in 2008
+
+Public documentation:
+
+- http://privatfrickler.de/blick-auf-den-chip-soundchip-general-instruments-ay-3-8910/
+ Die pictures of the AY8910
+
+- US Patent 4933980
+
+Games using ADSR: gyruss
+
+A list with more games using ADSR can be found here:
+ http://mametesters.org/view.php?id=3043
+
+TODO:
+* Measure volume / envelope parameters for AY8930 expanded mode
+* YM2610 & YM2608 will need a separate flag in their config structures
+ to distinguish between legacy and discrete mode.
+
+The rewrite also introduces a generic model for the DAC. This model is
+not perfect, but allows channel mixing based on a parametrized approach.
+This model also allows to factor in different loads on individual channels.
+If a better model is developed in the future or better measurements are
+available, the driver should be easy to change. The model is described
+later.
+
+In order to not break hundreds of existing drivers by default the flag
+AY8910_LEGACY_OUTPUT is used by drivers not changed to take into account the
+new model. All outputs are normalized to the old output range (i.e. 0 .. 7ffff).
+In the case of channel mixing, output range is 0...3 * 7fff.
+
+The main difference between the AY-3-8910 and the YM2149 is, that the
+AY-3-8910 datasheet mentions, that fixed volume level 0, which is set by
+registers 8 to 10 is "channel off". The YM2149 mentions, that the generated
+signal has a 2V DC component. This is confirmed by measurements. The approach
+taken here is to assume the 2V DC offset for all outputs for the YM2149.
+For the AY-3-8910, an offset is used if envelope is active for a channel.
+This is backed by oscilloscope pictures from the datasheet. If a fixed volume
+is set, i.e. envelope is disabled, the output voltage is set to 0V. Recordings
+I found on the web for gyruss indicate, that the AY-3-8910 offset should
+be around 0.2V. This will also make sound levels more compatible with
+user observations for scramble.
+
+The Model:
+ 5V 5V
+ | |
+ / |
+Volume Level x >---| Z
+ > Z Pullup Resistor RU
+ | Z
+ Z |
+ Rx Z |
+ Z |
+ | |
+ '-----+--------> >---+----> Output signal
+ | |
+ Z Z
+ Pulldown RD Z Z Load RL
+ Z Z
+ | |
+ GND GND
Each Volume level x will select a different resistor Rx. Measurements from fpgaarcade.com
where used to calibrate channel mixing for the YM2149. This was done using
@@ -228,29 +108,28 @@ has twice the steps, happening twice as fast.
****************************************************************************
- The bus control and chip selection signals of the AY PSGs and their
- pin-compatible clones such as YM2149 are somewhat unconventional and
- redundant, having been designed for compatibility with GI's CP1610
- series of microprocessors. Much of the redundancy can be finessed by
- tying BC2 to Vcc; AY-3-8913 and AY8930 do this internally.
-
- /A9 A8 /CS BDIR BC2 BC1
- AY-3-8910 24 25 n/a 27 28 29
- AY-3-8912 n/a 17 n/a 18 19 20
- AY-3-8913 22 23 24 2 n/a 3
- ------------------------------------
- Inactive NACT 0 0 0
- Latch address ADAR 0 0 1
- Inactive IAB 0 1 0
- Read from PSG DTB 0 1 1
- Latch address BAR 1 0 0
- Inactive DW 1 0 1
- Write to PSG DWS 1 1 0
- Latch address INTAK 1 1 1
-
-***************************************************************************/
-
-/**
+The bus control and chip selection signals of the AY PSGs and their
+pin-compatible clones such as YM2149 are somewhat unconventional and
+redundant, having been designed for compatibility with GI's CP1610
+series of microprocessors. Much of the redundancy can be finessed by
+tying BC2 to Vcc; AY-3-8913 and AY8930 do this internally.
+
+ /A9 A8 /CS BDIR BC2 BC1
+ AY-3-8910 24 25 n/a 27 28 29
+ AY-3-8912 n/a 17 n/a 18 19 20
+ AY-3-8913 22 23 24 2 n/a 3
+ ------------------------------------
+ Inactive NACT 0 0 0
+ Latch address ADAR 0 0 1
+ Inactive IAB 0 1 0
+ Read from PSG DTB 0 1 1
+ Latch address BAR 1 0 0
+ Inactive DW 1 0 1
+ Write to PSG DWS 1 1 0
+ Latch address INTAK 1 1 1
+
+****************************************************************************
+
AY-3-8910(A)/8914/8916/8917/8930/YM2149 (others?):
_______ _______
_| \__/ |_
@@ -579,11 +458,132 @@ AY-8930 datasheet: http://www.ym2149.com/ay8930.pdf
YM2149 datasheet: http://www.ym2149.com/ym2149.pdf
YM2203 English datasheet: http://www.appleii-box.de/APPLE2/JonasCard/YM2203%20datasheet.pdf
YM2203 Japanese datasheet contents, translated: http://www.larwe.com/technical/chip_ymopn.html
+
+****************************************************************************
+
+Couriersud, July 2014:
+
+This documents recent work on the AY8910. A YM2149 is now on it's way from
+Hong Kong as well.
+
+TODO:
+- Create a true sound device nAY8910 driver.
+- implement approach outlined below in this driver.
+
+For years I had a AY8910 in my drawer. Arduinos were around as well.
+Using the approach documented in this blog post
+ http://www.986-studio.com/2014/05/18/another-ay-entry/#more-476
+I measured the output voltages using a Extech 520.
+
+Measurement Setup
+
+Laptop <--> Arduino <---> AY8910
+
+AY8910 Registers:
+0x07: 3f
+0x08: RV
+0x09: RV
+0x0A: RV
+
+Output was measured on Analog Output B with a resistor RD to
+ground.
+
+Measurement results:
+
+RD 983 9.830k 99.5k 1.001M open
+
+RV B B B B B
+ 0 0.0000 0.0000 0.0001 0.0011 0.0616
+ 1 0.0106 0.0998 0.6680 1.8150 2.7260
+ 2 0.0150 0.1377 0.8320 1.9890 2.8120
+ 3 0.0222 0.1960 1.0260 2.1740 2.9000
+ 4 0.0320 0.2708 1.2320 2.3360 2.9760
+ 5 0.0466 0.3719 1.4530 2.4880 3.0440
+ 6 0.0665 0.4938 1.6680 2.6280 3.1130
+ 7 0.1039 0.6910 1.9500 2.7900 3.1860
+ 8 0.1237 0.7790 2.0500 2.8590 3.2340
+ 9 0.1986 1.0660 2.3320 3.0090 3.3090
+10 0.2803 1.3010 2.5050 3.0850 3.3380
+11 0.3548 1.4740 2.6170 3.1340 3.3590
+12 0.4702 1.6870 2.7340 3.1800 3.3730
+13 0.6030 1.8870 2.8410 3.2300 3.4050
+14 0.7530 2.0740 2.9280 3.2580 3.4170
+15 0.9250 2.2510 3.0040 3.2940 3.4380
+
+Using an equivalent model approach with two resistors
+
+ 5V
+ |
+ Z
+ Z Resistor Value for RV
+ Z
+ |
+ +---> Output signal
+ |
+ Z
+ Z External RD
+ Z
+ |
+ GND
+
+will NOT work out of the box since RV = RV(RD).
+
+The following approach will be used going forward based on die pictures
+of the AY8910 done by Dr. Stack van Hay:
+
+
+ 5V
+ _| D
+ G | NMOS
+ Vg ---|| Kn depends on volume selected
+ |_ S Vs
+ |
+ |
+ +---> VO Output signal
+ |
+ Z
+ Z External RD
+ Z
+ |
+ GND
+
+Whilst conducting, the FET operates in saturation mode:
+
+Id = Kn * (Vgs - Vth)^2
+
+Using Id = Vs / RD
+
+Vs = Kn * RD * (Vg - Vs - Vth)^2
+
+finally using Vg' = Vg - Vth
+
+Vs = Vg' + 1 / (2 * Kn * RD) - sqrt((Vg' + 1 / (2 * Kn * RD))^2 - Vg'^2)
+
+and finally
+
+VO = Vs
+
+and this can be used to re-Thenevin to 5V
+
+RVequiv = RD * ( 5V / VO - 1)
+
+The RV and Kn parameter are derived using least squares to match
+calculation results with measurements.
+
+FIXME:
+There is voltage of 60 mV measured with the EX520 (Ri ~ 10M). This may
+be induced by cutoff currents from the 15 FETs.
+
*/
#include "emu.h"
#include "ay8910.h"
+#define LOG_IGNORED_WRITES (1U << 1)
+#define LOG_WARNINGS (1U << 2)
+#define VERBOSE (LOG_WARNINGS)
+#include "logmacro.h"
+
/*************************************
*
* constants
@@ -591,16 +591,9 @@ YM2203 Japanese datasheet contents, translated: http://www.larwe.com/technical/c
*************************************/
#define ENABLE_REGISTER_TEST (0) // Enable preprogrammed registers
-#define LOG_IGNORED_WRITES (0)
static constexpr stream_buffer::sample_t MAX_OUTPUT = 1.0;
-/*************************************
- *
- * Type definitions
- *
- *************************************/
-
/*************************************
*
@@ -650,11 +643,9 @@ static const ay8910_device::ay_ym_param ay8910_param =
4120, 2512, 1737, 1335, 1005, 747, 586, 451 },
};
-/*
- * RL = 3000, Hacker Kay normalized pattern, 1.5V to 2.8V
- * These values correspond with guesses based on Gyruss schematics
- * They work well with scramble as well.
- */
+// RL = 3000, Hacker Kay normalized pattern, 1.5V to 2.8V
+// These values correspond with guesses based on Gyruss schematics
+// They work well with scramble as well.
static const ay8910_device::ay_ym_param ay8910_param =
{
930, 454,
@@ -663,12 +654,10 @@ static const ay8910_device::ay_ym_param ay8910_param =
4189, 2557, 1772, 1363, 1028, 766, 602, 464 },
};
-/*
- * RL = 1000, Hacker Kay normalized pattern, 0.75V to 2.05V
- * These values correspond with guesses based on Gyruss schematics
- * They work well with scramble as well.
- */
-static const ay8910_device::ay_ym_param ay8910_param =
+// RL = 1000, Hacker Kay normalized pattern, 0.75V to 2.05V
+// These values correspond with guesses based on Gyruss schematics
+// They work well with scramble as well.
+ tatic const ay8910_device::ay_ym_param ay8910_param =
{
1371, 313,
16,
@@ -676,9 +665,7 @@ static const ay8910_device::ay_ym_param ay8910_param =
3814, 2337, 1629, 1263, 962, 727, 580, 458 },
};
-/*
- * RL = 1000, Hacker Kay normalized pattern, 0.2V to 1.5V
- */
+// RL = 1000, Hacker Kay normalized pattern, 0.2V to 1.5V
static const ay8910_device::ay_ym_param ay8910_param =
{
5806, 300,
@@ -689,44 +676,45 @@ static const ay8910_device::ay_ym_param ay8910_param =
#endif
/*
- * RL = 2000, Based on Matthew Westcott's measurements from Dec 2001.
- * -------------------------------------------------------------------
- *
- * http://groups.google.com/group/comp.sys.sinclair/browse_thread/thread/fb3091da4c4caf26/d5959a800cda0b5e?lnk=gst&q=Matthew+Westcott#d5959a800cda0b5e
- * After what Russell mentioned a couple of weeks back about the lack of
- * publicised measurements of AY chip volumes - I've finally got round to
- * making these readings, and I'm placing them in the public domain - so
- * anyone's welcome to use them in emulators or anything else.
-
- * To make the readings, I set up the chip to produce a constant voltage on
- * channel C (setting bits 2 and 5 of register 6), and varied the amplitude
- * (the low 4 bits of register 10). The voltages were measured between the
- * channel C output (pin 1) and ground (pin 6).
- *
- * Level Voltage
- * 0 1.147
- * 1 1.162
- * 2 1.169
- * 3 1.178
- * 4 1.192
- * 5 1.213
- * 6 1.238
- * 7 1.299
- * 8 1.336
- * 9 1.457
- * 10 1.573
- * 11 1.707
- * 12 1.882
- * 13 2.06
- * 14 2.32
- * 15 2.58
- * -------------------------------------------------------------------
- *
- * The ZX spectrum output circuit was modelled in SwitcherCAD and
- * the resistor values below create the voltage levels above.
- * RD was measured on a real chip to be 8m Ohm, RU was 0.8m Ohm.
- */
+RL = 2000, Based on Matthew Westcott's measurements from Dec 2001.
+-------------------------------------------------------------------
+
+http://groups.google.com/group/comp.sys.sinclair/browse_thread/thread/fb3091da4c4caf26/d5959a800cda0b5e?lnk=gst&q=Matthew+Westcott#d5959a800cda0b5e
+After what Russell mentioned a couple of weeks back about the lack of
+publicised measurements of AY chip volumes - I've finally got round to
+making these readings, and I'm placing them in the public domain - so
+anyone's welcome to use them in emulators or anything else.
+
+To make the readings, I set up the chip to produce a constant voltage on
+channel C (setting bits 2 and 5 of register 6), and varied the amplitude
+(the low 4 bits of register 10). The voltages were measured between the
+channel C output (pin 1) and ground (pin 6).
+
+Level Voltage
+ 0 1.147
+ 1 1.162
+ 2 1.169
+ 3 1.178
+ 4 1.192
+ 5 1.213
+ 6 1.238
+ 7 1.299
+ 8 1.336
+ 9 1.457
+10 1.573
+11 1.707
+12 1.882
+13 2.06
+14 2.32
+15 2.58
+-------------------------------------------------------------------
+
+The ZX spectrum output circuit was modelled in SwitcherCAD and
+the resistor values below create the voltage levels above.
+RD was measured on a real chip to be 8m Ohm, RU was 0.8m Ohm.
+
+*/
static const ay8910_device::ay_ym_param ay8910_param =
{
@@ -763,7 +751,6 @@ static const ay8910_device::mosfet_param ay8910_mosfet_param =
-
/*************************************
*
* Inline
@@ -972,7 +959,7 @@ void ay8910_device::ay8910_write_reg(int r, int v)
((m_last_enable & 0x40) != (m_regs[AY_ENABLE] & 0x40)))
{
// write out 0xff if port set to input
- if (!m_port_a_write_cb.isnull())
+ if (!m_port_a_write_cb.isunset())
m_port_a_write_cb((offs_t)0, (m_regs[AY_ENABLE] & 0x40) ? m_regs[AY_PORTA] : 0xff);
}
@@ -980,7 +967,7 @@ void ay8910_device::ay8910_write_reg(int r, int v)
((m_last_enable & 0x80) != (m_regs[AY_ENABLE] & 0x80)))
{
// write out 0xff if port set to input
- if (!m_port_b_write_cb.isnull())
+ if (!m_port_b_write_cb.isunset())
m_port_b_write_cb((offs_t)0, (m_regs[AY_ENABLE] & 0x80) ? m_regs[AY_PORTB] : 0xff);
}
m_last_enable = m_regs[AY_ENABLE];
@@ -1001,7 +988,7 @@ void ay8910_device::ay8910_write_reg(int r, int v)
}
}
else if (m_mode & 0xf)
- logerror("warning: activated unknown mode %02x at %s\n", m_mode & 0xf, name());
+ LOGMASKED(LOG_WARNINGS, "%s: warning: activated unknown mode %02x at %s\n", machine().describe_context(), m_mode & 0xf, name());
}
}
m_envelope[0].set_shape(m_regs[AY_EASHAPE], m_env_step_mask);
@@ -1009,31 +996,27 @@ void ay8910_device::ay8910_write_reg(int r, int v)
case AY_PORTA:
if (m_regs[AY_ENABLE] & 0x40)
{
- if (!m_port_a_write_cb.isnull())
+ if (!m_port_a_write_cb.isunset())
m_port_a_write_cb((offs_t)0, m_regs[AY_PORTA]);
else
- logerror("warning: unmapped write %02x to %s Port A\n", v, name());
+ LOGMASKED(LOG_WARNINGS, "%s: warning: unmapped write %02x to %s Port A\n", machine().describe_context(), v, name());
}
else
{
-#if LOG_IGNORED_WRITES
- logerror("warning: write %02x to %s Port A set as input - ignored\n", v, name());
-#endif
+ LOGMASKED(LOG_IGNORED_WRITES, "%s: warning: write %02x to %s Port A set as input - ignored\n", machine().describe_context(), v, name());
}
break;
case AY_PORTB:
if (m_regs[AY_ENABLE] & 0x80)
{
- if (!m_port_b_write_cb.isnull())
+ if (!m_port_b_write_cb.isunset())
m_port_b_write_cb((offs_t)0, m_regs[AY_PORTB]);
else
- logerror("warning: unmapped write %02x to %s Port B\n", v, name());
+ LOGMASKED(LOG_WARNINGS, "%s: warning: unmapped write %02x to %s Port B\n", machine().describe_context(), v, name());
}
else
{
-#if LOG_IGNORED_WRITES
- logerror("warning: write %02x to %s Port B set as input - ignored\n", v, name());
-#endif
+ LOGMASKED(LOG_IGNORED_WRITES, "%s: warning: write %02x to %s Port B set as input - ignored\n", machine().describe_context(), v, name());
}
break;
case AY_EBFINE:
@@ -1232,7 +1215,7 @@ void ay8910_device::build_mixer_table()
if ((m_flags & AY8910_LEGACY_OUTPUT) != 0)
{
- logerror("%s using legacy output levels!\n", name());
+ LOGMASKED(LOG_WARNINGS, "%s using legacy output levels!\n", name());
normalize = 1;
}
@@ -1304,20 +1287,15 @@ void ay8910_device::device_start()
{
const int master_clock = clock();
- if (m_ioports < 1 && !(m_port_a_read_cb.isnull() && m_port_a_write_cb.isnull()))
+ if (m_ioports < 1 && !(m_port_a_read_cb.isunset() && m_port_a_write_cb.isunset()))
fatalerror("Device '%s' is a %s and has no port A!", tag(), name());
- if (m_ioports < 2 && !(m_port_b_read_cb.isnull() && m_port_b_write_cb.isnull()))
+ if (m_ioports < 2 && !(m_port_b_read_cb.isunset() && m_port_b_write_cb.isunset()))
fatalerror("Device '%s' is a %s and has no port B!", tag(), name());
- m_port_a_read_cb.resolve();
- m_port_b_read_cb.resolve();
- m_port_a_write_cb.resolve();
- m_port_b_write_cb.resolve();
-
if ((m_flags & AY8910_SINGLE_OUTPUT) != 0)
{
- logerror("%s device using single output!\n", name());
+ LOGMASKED(LOG_WARNINGS, "%s device using single output!\n", name());
m_streams = 1;
}
@@ -1419,7 +1397,7 @@ void ay8910_device::ay8910_write_ym(int addr, u8 data)
}
else
{
- logerror("%s: warning - %s upper address mismatch\n", machine().describe_context(), name());
+ LOGMASKED(LOG_WARNINGS, "%s: warning - %s upper address mismatch\n", machine().describe_context(), name());
}
}
}
@@ -1441,7 +1419,7 @@ u8 ay8910_device::ay8910_read_ym()
{
case AY_PORTA:
if ((m_regs[AY_ENABLE] & 0x40) != 0)
- logerror("%s: warning - read from %s Port A set as output\n", machine().describe_context(), name());
+ LOGMASKED(LOG_WARNINGS, "%s: warning - read from %s Port A set as output\n", machine().describe_context(), name());
/*
even if the port is set as output, we still need to return the external
data. Some games, like kidniki, need this to work.
@@ -1451,19 +1429,19 @@ u8 ay8910_device::ay8910_read_ym()
specifies those pull up resistors as 60k to 600k (min / max).
We do need a callback for those two flags. Kid Niki (Irem m62) is one such
case were it makes a difference in comparison to a standard TTL output.
- */
- if (!m_port_a_read_cb.isnull())
+ */
+ if (!m_port_a_read_cb.isunset())
m_regs[AY_PORTA] = m_port_a_read_cb(0);
else
- logerror("%s: warning - read 8910 Port A\n", machine().describe_context());
+ LOGMASKED(LOG_WARNINGS, "%s: warning - read 8910 Port A\n", machine().describe_context());
break;
case AY_PORTB:
if ((m_regs[AY_ENABLE] & 0x80) != 0)
- logerror("%s: warning - read from 8910 Port B set as output\n", machine().describe_context());
- if (!m_port_b_read_cb.isnull())
+ LOGMASKED(LOG_WARNINGS, "%s: warning - read from 8910 Port B set as output\n", machine().describe_context());
+ if (!m_port_b_read_cb.isunset())
m_regs[AY_PORTB] = m_port_b_read_cb(0);
else
- logerror("%s: warning - read 8910 Port B\n", machine().describe_context());
+ LOGMASKED(LOG_WARNINGS, "%s: warning - read 8910 Port B\n", machine().describe_context());
break;
}
@@ -1506,6 +1484,7 @@ void ay8910_device::device_reset()
ay8910_reset_ym();
}
+
/*************************************
*
* Read/Write Handlers
@@ -1593,35 +1572,34 @@ ay8910_device::ay8910_device(const machine_config &mconfig, const char *tag, dev
{
}
-ay8910_device::ay8910_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock,
- psg_type_t psg_type, int streams, int ioports, int feature)
- : device_t(mconfig, type, tag, owner, clock),
- device_sound_interface(mconfig, *this),
- m_type(psg_type),
- m_streams(streams),
- m_ioports(ioports),
- m_ready(0),
- m_channel(nullptr),
- m_active(false),
- m_register_latch(0),
- m_last_enable(0),
- m_prescale_noise(0),
- m_noise_value(0),
- m_count_noise(0),
- m_rng(0),
- m_noise_out(0),
- m_mode(0),
- m_env_step_mask((!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 0x0f : 0x1f),
- m_step( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 2 : 1),
- m_zero_is_off( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 1 : 0),
- m_par( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? &ay8910_param : &ym2149_param),
- m_par_env( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? &ay8910_param : &ym2149_param_env),
- m_flags(AY8910_LEGACY_OUTPUT),
- m_feature(feature),
- m_port_a_read_cb(*this),
- m_port_b_read_cb(*this),
- m_port_a_write_cb(*this),
- m_port_b_write_cb(*this)
+ay8910_device::ay8910_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, psg_type_t psg_type, int streams, int ioports, int feature) :
+ device_t(mconfig, type, tag, owner, clock),
+ device_sound_interface(mconfig, *this),
+ m_type(psg_type),
+ m_streams(streams),
+ m_ioports(ioports),
+ m_ready(0),
+ m_channel(nullptr),
+ m_active(false),
+ m_register_latch(0),
+ m_last_enable(0),
+ m_prescale_noise(0),
+ m_noise_value(0),
+ m_count_noise(0),
+ m_rng(0),
+ m_noise_out(0),
+ m_mode(0),
+ m_env_step_mask((!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 0x0f : 0x1f),
+ m_step( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 2 : 1),
+ m_zero_is_off( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 1 : 0),
+ m_par( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? &ay8910_param : &ym2149_param),
+ m_par_env( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? &ay8910_param : &ym2149_param_env),
+ m_flags(AY8910_LEGACY_OUTPUT),
+ m_feature(feature),
+ m_port_a_read_cb(*this, 0xff),
+ m_port_b_read_cb(*this, 0xff),
+ m_port_a_write_cb(*this),
+ m_port_b_write_cb(*this)
{
memset(&m_regs,0,sizeof(m_regs));
memset(&m_tone,0,sizeof(m_tone));
diff --git a/src/devices/sound/beep.cpp b/src/devices/sound/beep.cpp
index 47f4701a662..d2358d4cc93 100644
--- a/src/devices/sound/beep.cpp
+++ b/src/devices/sound/beep.cpp
@@ -12,7 +12,7 @@
****************************************************************************/
#include "emu.h"
-#include "sound/beep.h"
+#include "beep.h"
#define BEEP_RATE (384000)
@@ -65,7 +65,7 @@ void beep_device::sound_stream_update(sound_stream &stream, std::vector<read_str
clock = m_frequency;
/* if we're not enabled, just fill with 0 */
- if ( !m_enable || clock == 0 )
+ if (!m_enable || clock == 0)
{
buffer.fill(0);
return;
@@ -93,7 +93,7 @@ void beep_device::sound_stream_update(sound_stream &stream, std::vector<read_str
// changing state to on from off will restart tone
//-------------------------------------------------
-WRITE_LINE_MEMBER(beep_device::set_state)
+void beep_device::set_state(int state)
{
/* only update if new state is not the same as old state */
int on = (state) ? 1 : 0;
diff --git a/src/devices/sound/beep.h b/src/devices/sound/beep.h
index 52943d83465..4ea9064c49c 100644
--- a/src/devices/sound/beep.h
+++ b/src/devices/sound/beep.h
@@ -31,15 +31,15 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
public:
- DECLARE_WRITE_LINE_MEMBER(set_state); // enable/disable sound output
- void set_clock(uint32_t frequency); // output frequency
+ void set_state(int state); // enable/disable sound output
+ void set_clock(uint32_t frequency); // output frequency
private:
- sound_stream *m_stream; /* stream number */
- int m_enable; /* enable beep */
- int m_frequency; /* set frequency - this can be changed using the appropriate function */
- int m_incr; /* initial wave state */
- stream_buffer::sample_t m_signal; /* current signal */
+ sound_stream *m_stream; // stream number
+ int m_enable; // enable beep
+ int m_frequency; // set frequency - this can be changed using the appropriate function
+ int m_incr; // initial wave state
+ stream_buffer::sample_t m_signal; // current signal
};
DECLARE_DEVICE_TYPE(BEEP, beep_device)
diff --git a/src/devices/sound/bsmt2000.cpp b/src/devices/sound/bsmt2000.cpp
index ee7e4e4ee86..428009b1fc2 100644
--- a/src/devices/sound/bsmt2000.cpp
+++ b/src/devices/sound/bsmt2000.cpp
@@ -110,7 +110,7 @@ void bsmt2000_device::device_add_mconfig(machine_config &config)
void bsmt2000_device::device_start()
{
- m_ready_callback.resolve();
+ m_ready_callback.resolve_safe();
// create the stream; BSMT typically runs at 24MHz and writes to a DAC, so
// in theory we should generate a 24MHz stream, but that's certainly overkill
@@ -196,10 +196,11 @@ void bsmt2000_device::sound_stream_update(sound_stream &stream, std::vector<read
//-------------------------------------------------
-// rom_bank_updated - the rom bank has changed
+// rom_bank_pre_change - refresh the stream if the
+// ROM banking changes
//-------------------------------------------------
-void bsmt2000_device::rom_bank_updated()
+void bsmt2000_device::rom_bank_pre_change()
{
m_stream->update();
}
@@ -236,7 +237,7 @@ void bsmt2000_device::write_data(uint16_t data)
m_deferred_data_write->adjust(attotime::zero, data);
// boost the interleave on a write so that the caller detects the status more accurately
- machine().scheduler().boost_interleave(attotime::from_usec(1), attotime::from_usec(10));
+ machine().scheduler().add_quantum(attotime::from_usec(1), attotime::from_usec(10));
}
@@ -260,8 +261,7 @@ uint16_t bsmt2000_device::tms_data_r()
{
// also implicitly clear the write pending flag
m_write_pending = false;
- if (!m_ready_callback.isnull())
- m_ready_callback();
+ m_ready_callback();
return m_write_data;
}
@@ -330,7 +330,7 @@ void bsmt2000_device::tms_right_w(uint16_t data)
// on the TMS32015
//-------------------------------------------------
-READ_LINE_MEMBER( bsmt2000_device::tms_write_pending_r )
+int bsmt2000_device::tms_write_pending_r()
{
return m_write_pending ? 1 : 0;
}
diff --git a/src/devices/sound/bsmt2000.h b/src/devices/sound/bsmt2000.h
index 459a83bb86c..333e6d90e7c 100644
--- a/src/devices/sound/bsmt2000.h
+++ b/src/devices/sound/bsmt2000.h
@@ -55,7 +55,7 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
// device_rom_interface overrides
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
TIMER_CALLBACK_MEMBER(deferred_reset);
TIMER_CALLBACK_MEMBER(deferred_reg_write);
@@ -89,7 +89,7 @@ private:
emu_timer * m_deferred_reg_write;
emu_timer * m_deferred_data_write;
- DECLARE_READ_LINE_MEMBER( tms_write_pending_r );
+ int tms_write_pending_r();
};
diff --git a/src/devices/sound/c140.cpp b/src/devices/sound/c140.cpp
index 28eea0d508b..688ebec6459 100644
--- a/src/devices/sound/c140.cpp
+++ b/src/devices/sound/c140.cpp
@@ -1,6 +1,7 @@
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/*
+
c140.cpp
Simulator based on AMUSE sources.
@@ -10,6 +11,11 @@ This chip controls 24 channels (C140) or 16 (219) of PCM.
16 bytes are associated with each channel.
Channels can be 8 bit compressed PCM, or 12 bit signed PCM.
+2000.06.26: CAB fixed compressed pcm playback
+2002.07.20: R. Belmont added support for multiple banking types
+2006.01.08: R. Belmont added support for NA-1/2 "219" derivative
+2020.05.06: cam900 implemented some features from QuattroPlay sources, by superctr
+
TODO:
- What does the INT0 pin do? Normally Namco tied it to VOL0 (with VOL1 = VCC).
- Acknowledge A9 bit (9th address bit) of host interface
@@ -19,35 +25,30 @@ TODO:
--------------
- ASIC "219" notes
+ASIC "219" notes
- On the 219 ASIC used on NA-1 and NA-2, the high registers have the following
- meaning instead:
- 0x1f7: bank for voices 0-3
- 0x1f1: bank for voices 4-7
- 0x1f3: bank for voices 8-11
- 0x1f5: bank for voices 12-15
+On the 219 ASIC used on NA-1 and NA-2, the high registers have the following
+meaning instead:
+0x1f7: bank for voices 0-3
+0x1f1: bank for voices 4-7
+0x1f3: bank for voices 8-11
+0x1f5: bank for voices 12-15
- Some games (bkrtmaq, xday2) write to 0x1fd for voices 12-15 instead. Probably the bank registers
- mirror at 1f8, in which case 1ff is also 0-3, 1f9 is also 4-7, 1fb is also 8-11, and 1fd is also 12-15.
+Some games (bkrtmaq, xday2) write to 0x1fd for voices 12-15 instead. Probably the bank registers
+mirror at 1f8, in which case 1ff is also 0-3, 1f9 is also 4-7, 1fb is also 8-11, and 1fd is also 12-15.
- Each bank is 0x20000 (128k), and the voice addresses on the 219 are all multiplied by 2.
- Additionally, the 219's base pitch is the same as the C352's (42667). But these changes
- are IMO not sufficient to make this a separate file - all the other registers are
- fully compatible.
+Each bank is 0x20000 (128k), and the voice addresses on the 219 are all multiplied by 2.
+Additionally, the 219's base pitch is the same as the C352's (42667). But these changes
+are IMO not sufficient to make this a separate file - all the other registers are
+fully compatible.
- Finally, the 219 only has 16 voices.
-*/
-/*
- 2000.06.26 CAB fixed compressed pcm playback
- 2002.07.20 R. Belmont added support for multiple banking types
- 2006.01.08 R. Belmont added support for NA-1/2 "219" derivative
- 2020.05.06 cam900 Implement some features from QuattroPlay sources, by superctr
-*/
+Finally, the 219 only has 16 voices.
+*/
#include "emu.h"
#include "c140.h"
+
#include <algorithm>
struct voice_registers
@@ -116,7 +117,6 @@ void c140_device::device_start()
{
m_sample_rate = m_baserate = clock();
- m_int1_callback.resolve_safe();
m_int1_timer = timer_alloc(FUNC(c140_device::int1_on), this);
m_stream = stream_alloc(0, 2, m_sample_rate);
@@ -167,6 +167,7 @@ void c140_device::device_start()
void c219_device::device_start()
{
c140_device::device_start();
+
// generate mulaw table (Verified from Wii Virtual Console Arcade Knuckle Heads)
// same as c352.cpp
int j = 0;
@@ -204,7 +205,7 @@ void c140_device::device_clock_changed()
}
-void c140_device::rom_bank_updated()
+void c140_device::rom_bank_pre_change()
{
m_stream->update();
}
@@ -216,11 +217,8 @@ void c140_device::rom_bank_updated()
void c140_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
{
- s32 dt;
-
- float pbase = (float)m_baserate * 2.0f / (float)m_sample_rate;
-
- s16 *lmix, *rmix;
+ float pbase = (float)m_baserate * 2.0f / (float)m_sample_rate;
+ s16 *lmix, *rmix;
int samples = outputs[0].samples();
if (samples > m_sample_rate) samples = m_sample_rate;
@@ -299,7 +297,7 @@ void c140_device::sound_stream_update(sound_stream &stream, std::vector<read_str
}
/* Caclulate the sample value */
- dt = ((dltdt * offset) >> 16) + prevdt;
+ s32 dt = ((dltdt * offset) >> 16) + prevdt;
/* Write the data to the sample buffers */
*lmix++ += (dt * lvol) >> (5 + 4);
@@ -331,11 +329,8 @@ void c140_device::sound_stream_update(sound_stream &stream, std::vector<read_str
void c219_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
{
- s32 dt;
-
- float pbase = (float)m_baserate * 2.0f / (float)m_sample_rate;
-
- s16 *lmix, *rmix;
+ float pbase = (float)m_baserate * 2.0f / (float)m_sample_rate;
+ s16 *lmix, *rmix;
int samples = outputs[0].samples();
if (samples > m_sample_rate) samples = m_sample_rate;
@@ -433,7 +428,7 @@ void c219_device::sound_stream_update(sound_stream &stream, std::vector<read_str
}
/* Caclulate the sample value */
- dt = ((dltdt * offset) >> 16) + prevdt;
+ s32 dt = ((dltdt * offset) >> 16) + prevdt;
/* Write the data to the sample buffers */
*lmix++ += ((ch_inv_lout(v)) ? -(dt * lvol) : (dt * lvol)) >> (5 + shift);
@@ -478,11 +473,19 @@ inline u8 c140_device::keyon_status_read(u16 offset)
u8 c140_device::c140_r(offs_t offset)
{
offset &= 0x1ff;
+ u8 data = m_REG[offset];
if ((offset & 0xf) == 0x5 && offset < 0x180)
- return keyon_status_read(offset);
+ {
+ data = keyon_status_read(offset);
+ }
+ else if (offset == 0x1f8)
+ {
+ // timer reload value = written reg data + 1
+ data++;
+ }
- return m_REG[offset];
+ return data;
}
@@ -525,29 +528,39 @@ void c140_device::c140_w(offs_t offset, u8 data)
}
}
}
- else if (offset == 0x1fa)
- {
- m_int1_callback(CLEAR_LINE);
- // timing not verified
- unsigned div = m_REG[0x1f8] != 0 ? m_REG[0x1f8] : 256;
- attotime interval = attotime::from_ticks(div * 2, m_baserate);
- if (BIT(m_REG[0x1fe], 0))
- m_int1_timer->adjust(interval);
- }
- else if (offset == 0x1fe)
+ else switch (offset)
{
- if (BIT(data, 0))
- {
- // kyukaidk and marvlandj want the first interrupt to happen immediately
- if (!m_int1_timer->enabled())
- m_int1_callback(ASSERT_LINE);
- }
- else
- {
+ // timer reload value
+ case 0x1f8:
+ break;
+
+ // set INT1 timer
+ case 0x1fa:
m_int1_callback(CLEAR_LINE);
- m_int1_timer->enable(false);
- }
+
+ if (BIT(m_REG[0x1fe], 0))
+ m_int1_timer->adjust(attotime::from_ticks((m_REG[0x1f8] + 1) * 2, m_baserate));
+
+ break;
+
+ // enable INT1 timer
+ case 0x1fe:
+ if (BIT(data, 0))
+ {
+ // kyukaidk and marvlandj want the first interrupt to happen immediately
+ if (m_int1_timer->expire().is_never())
+ m_int1_callback(ASSERT_LINE);
+ }
+ else
+ {
+ m_int1_callback(CLEAR_LINE);
+ m_int1_timer->adjust(attotime::never);
+ }
+ break;
+
+ default:
+ break;
}
}
@@ -556,12 +569,16 @@ u8 c219_device::c219_r(offs_t offset)
{
offset &= 0x1ff;
- // assume same as c140
// TODO: what happens here on reading unmapped voice regs?
+ u8 data = m_REG[offset];
+
if ((offset & 0xf) == 0x5 && offset < 0x100)
- return keyon_status_read(offset);
+ {
+ // assume same as c140
+ data = keyon_status_read(offset);
+ }
- return m_REG[offset];
+ return data;
}
diff --git a/src/devices/sound/c140.h b/src/devices/sound/c140.h
index abbe72b1053..51dd226acea 100644
--- a/src/devices/sound/c140.h
+++ b/src/devices/sound/c140.h
@@ -41,7 +41,7 @@ protected:
virtual void device_start() override;
virtual void device_clock_changed() override;
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
// sound stream update overrides
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
@@ -94,7 +94,7 @@ protected:
int m_baserate;
u8 m_REG[0x200];
- s16 m_pcmtbl[256]; //2000.06.26 CAB
+ s16 m_pcmtbl[256];
C140_VOICE m_voi[MAX_VOICE];
diff --git a/src/devices/sound/c352.cpp b/src/devices/sound/c352.cpp
index bea5a561b59..dcbd58f3eb3 100644
--- a/src/devices/sound/c352.cpp
+++ b/src/devices/sound/c352.cpp
@@ -50,10 +50,11 @@ c352_device::c352_device(const machine_config &mconfig, const char *tag, device_
}
//-------------------------------------------------
-// rom_bank_updated - the rom bank has changed
+// rom_bank_pre_change - refresh the stream if the
+// ROM banking changes
//-------------------------------------------------
-void c352_device::rom_bank_updated()
+void c352_device::rom_bank_pre_change()
{
m_stream->update();
}
diff --git a/src/devices/sound/c352.h b/src/devices/sound/c352.h
index d21ac96cd86..cf333ba356e 100644
--- a/src/devices/sound/c352.h
+++ b/src/devices/sound/c352.h
@@ -42,7 +42,7 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
// device_rom_interface overrides
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
private:
enum
diff --git a/src/devices/sound/cdda.cpp b/src/devices/sound/cdda.cpp
index 38a4c4736cc..cc222534f98 100644
--- a/src/devices/sound/cdda.cpp
+++ b/src/devices/sound/cdda.cpp
@@ -18,8 +18,6 @@
void cdda_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
{
get_audio_data(outputs[0], outputs[1]);
- m_audio_volume[0] = outputs[0].get(0);
- m_audio_volume[1] = outputs[1].get(0);
}
//-------------------------------------------------
@@ -40,7 +38,7 @@ void cdda_device::device_start()
m_audio_length = 0;
m_audio_samples = 0;
m_audio_bptr = 0;
- m_disc = nullptr;
+ m_sequence_counter = 0;
save_item( NAME(m_audio_playing) );
save_item( NAME(m_audio_pause) );
@@ -50,17 +48,8 @@ void cdda_device::device_start()
save_pointer( NAME(m_audio_cache), cdrom_file::MAX_SECTOR_DATA * MAX_SECTORS );
save_item( NAME(m_audio_samples) );
save_item( NAME(m_audio_bptr) );
-}
-
+ save_item( NAME(m_sequence_counter) );
-/*-------------------------------------------------
- cdda_set_cdrom - set the CD-ROM file for the
- given CDDA stream
--------------------------------------------------*/
-
-void cdda_device::set_cdrom(void *file)
-{
- m_disc = (cdrom_file *)file;
}
@@ -168,14 +157,17 @@ void cdda_device::get_audio_data(write_stream_view &bufL, write_stream_view &buf
{
/* if no file, audio not playing, audio paused, or out of disc data,
just zero fill */
- if (!m_disc || !m_audio_playing || m_audio_pause || (!m_audio_length && !m_audio_samples))
+ if (m_disc->sequence_counter() != m_sequence_counter || !m_disc->exists() || !m_audio_playing || m_audio_pause || (!m_audio_length && !m_audio_samples))
{
- if( m_disc && m_audio_playing && !m_audio_pause && !m_audio_length )
+ if( m_audio_playing && !m_audio_pause && !m_audio_length )
{
m_audio_playing = false;
m_audio_ended_normally = true;
+ m_audio_end_cb(ASSERT_LINE);
}
+ m_sequence_counter = m_disc->sequence_counter();
+ m_audio_data[0] = m_audio_data[1] = 0;
bufL.fill(0, sampindex);
bufR.fill(0, sampindex);
return;
@@ -190,8 +182,12 @@ void cdda_device::get_audio_data(write_stream_view &bufL, write_stream_view &buf
for (i = 0; i < samples; i++)
{
/* CD-DA data on the disc is big-endian */
- bufL.put_int(sampindex + i, s16(big_endianize_int16( audio_cache[ m_audio_bptr ] )), 32768); m_audio_bptr++;
- bufR.put_int(sampindex + i, s16(big_endianize_int16( audio_cache[ m_audio_bptr ] )), 32768); m_audio_bptr++;
+ m_audio_data[0] = s16(big_endianize_int16( audio_cache[ m_audio_bptr ] ));
+ bufL.put_int(sampindex + i, m_audio_data[0], 32768);
+ m_audio_bptr++;
+ m_audio_data[1] = s16(big_endianize_int16( audio_cache[ m_audio_bptr ] ));
+ bufR.put_int(sampindex + i, m_audio_data[1], 32768);
+ m_audio_bptr++;
}
sampindex += samples;
@@ -222,13 +218,16 @@ void cdda_device::get_audio_data(write_stream_view &bufL, write_stream_view &buf
}
/*-------------------------------------------------
- cdda_get_channel_volume - sets CD-DA volume level
- for either speaker, used for volume control display
+ get_channel_sample - reads currently decoded
+ data sample on the stream.
+ Used by PC Engine CD class family for volume
+ metering on audio CD player.
-------------------------------------------------*/
-int16_t cdda_device::get_channel_volume(int channel)
+int16_t cdda_device::get_channel_sample(int channel)
{
- return m_audio_volume[channel];
+ m_stream->update();
+ return m_audio_data[channel];
}
DEFINE_DEVICE_TYPE(CDDA, cdda_device, "cdda", "CD/DA")
@@ -236,7 +235,8 @@ DEFINE_DEVICE_TYPE(CDDA, cdda_device, "cdda", "CD/DA")
cdda_device::cdda_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, CDDA, tag, owner, clock)
, device_sound_interface(mconfig, *this)
- , m_disc(nullptr)
+ , m_disc(*this, finder_base::DUMMY_TAG)
, m_stream(nullptr)
+ , m_audio_end_cb(*this)
{
}
diff --git a/src/devices/sound/cdda.h b/src/devices/sound/cdda.h
index b0c7b7f81e8..1d8921f688e 100644
--- a/src/devices/sound/cdda.h
+++ b/src/devices/sound/cdda.h
@@ -5,26 +5,28 @@
#pragma once
-#include "cdrom.h"
+#include "imagedev/cdromimg.h"
class cdda_device : public device_t, public device_sound_interface
{
public:
- cdda_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 44'100);
+ cdda_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 44100);
- void set_cdrom(void *file);
+ template<typename T> void set_cdrom_tag(T &&tag) { m_disc.set_tag(std::forward<T>(tag)); }
void start_audio(uint32_t startlba, uint32_t numblocks);
void stop_audio();
void pause_audio(int pause);
- int16_t get_channel_volume(int channel);
+ int16_t get_channel_sample(int channel);
uint32_t get_audio_lba();
int audio_active();
int audio_paused();
int audio_ended();
+ auto audio_end_cb() { return m_audio_end_cb.bind(); }
+
protected:
// device-level overrides
virtual void device_start() override;
@@ -35,7 +37,7 @@ protected:
private:
void get_audio_data(write_stream_view &bufL, write_stream_view &bufR);
- cdrom_file * m_disc;
+ required_device<cdrom_image_device> m_disc;
// internal state
sound_stream * m_stream;
@@ -46,7 +48,11 @@ private:
std::unique_ptr<uint8_t[]> m_audio_cache;
uint32_t m_audio_samples;
uint32_t m_audio_bptr;
- int16_t m_audio_volume[2];
+ int16_t m_audio_data[2];
+
+ uint32_t m_sequence_counter;
+
+ devcb_write_line m_audio_end_cb;
};
DECLARE_DEVICE_TYPE(CDDA, cdda_device)
diff --git a/src/devices/sound/cdp1864.cpp b/src/devices/sound/cdp1864.cpp
index a3e8f5c037f..2d8acb64358 100644
--- a/src/devices/sound/cdp1864.cpp
+++ b/src/devices/sound/cdp1864.cpp
@@ -58,24 +58,24 @@ DEFINE_DEVICE_TYPE(CDP1864, cdp1864_device, "cdp1864", "RCA CDP1864")
// cdp1864_device - constructor
//-------------------------------------------------
-cdp1864_device::cdp1864_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, CDP1864, tag, owner, clock),
- device_sound_interface(mconfig, *this),
- device_video_interface(mconfig, *this),
- m_read_inlace(*this),
- m_read_rdata(*this),
- m_read_bdata(*this),
- m_read_gdata(*this),
- m_write_int(*this),
- m_write_dma_out(*this),
- m_write_efx(*this),
- m_write_hsync(*this),
- m_disp(0),
- m_dmaout(0),
- m_bgcolor(0),
- m_con(0),
- m_aoe(0),
- m_latch(CDP1864_DEFAULT_LATCH)
+cdp1864_device::cdp1864_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, CDP1864, tag, owner, clock),
+ device_sound_interface(mconfig, *this),
+ device_video_interface(mconfig, *this),
+ m_read_inlace(*this, 1),
+ m_read_rdata(*this, 0),
+ m_read_bdata(*this, 0),
+ m_read_gdata(*this, 0),
+ m_write_int(*this),
+ m_write_dma_out(*this),
+ m_write_efx(*this),
+ m_write_hsync(*this),
+ m_disp(0),
+ m_dmaout(0),
+ m_bgcolor(0),
+ m_con(0),
+ m_aoe(0),
+ m_latch(CDP1864_DEFAULT_LATCH)
{
}
@@ -105,16 +105,6 @@ void cdp1864_device::device_config_complete()
void cdp1864_device::device_start()
{
- // resolve callbacks
- m_read_inlace.resolve_safe(1);
- m_read_rdata.resolve_safe(0);
- m_read_bdata.resolve_safe(0);
- m_read_gdata.resolve_safe(0);
- m_write_int.resolve_safe();
- m_write_dma_out.resolve_safe();
- m_write_efx.resolve_safe();
- m_write_hsync.resolve_safe();
-
// initialize palette
initialize_palette();
diff --git a/src/devices/sound/cdp1869.cpp b/src/devices/sound/cdp1869.cpp
index 70e4718f212..c0a54adaf99 100644
--- a/src/devices/sound/cdp1869.cpp
+++ b/src/devices/sound/cdp1869.cpp
@@ -159,13 +159,7 @@ void cdp1869_device::write_page_ram_byte(offs_t pma, uint8_t data)
uint8_t cdp1869_device::read_char_ram_byte(offs_t pma, offs_t cma, uint8_t pmd)
{
- uint8_t data = 0;
-
- if (!m_in_char_ram_func.isnull())
- {
- data = m_in_char_ram_func(pma, cma, pmd);
- }
-
+ uint8_t const data = m_in_char_ram_func(pma, cma, pmd);
return data;
}
@@ -177,10 +171,7 @@ uint8_t cdp1869_device::read_char_ram_byte(offs_t pma, offs_t cma, uint8_t pmd)
void cdp1869_device::write_char_ram_byte(offs_t pma, offs_t cma, uint8_t pmd, uint8_t data)
{
- if (!m_out_char_ram_func.isnull())
- {
- m_out_char_ram_func(pma, cma, pmd, data);
- }
+ m_out_char_ram_func(pma, cma, pmd, data);
}
@@ -190,13 +181,7 @@ void cdp1869_device::write_char_ram_byte(offs_t pma, offs_t cma, uint8_t pmd, ui
int cdp1869_device::read_pcb(offs_t pma, offs_t cma, uint8_t pmd)
{
- int pcb = 0;
-
- if (!m_in_pcb_func.isnull())
- {
- pcb = m_in_pcb_func(pma, cma, pmd);
- }
-
+ int const pcb = m_in_pcb_func(pma, cma, pmd);
return pcb;
}
@@ -355,7 +340,7 @@ cdp1869_device::cdp1869_device(const machine_config &mconfig, const char *tag, d
device_sound_interface(mconfig, *this),
device_video_interface(mconfig, *this),
device_memory_interface(mconfig, *this),
- m_read_pal_ntsc(*this),
+ m_read_pal_ntsc(*this, 0),
m_write_prd(*this),
m_in_pcb_func(*this),
m_in_char_ram_func(*this),
@@ -385,11 +370,9 @@ void cdp1869_device::device_add_mconfig(machine_config &config)
void cdp1869_device::device_start()
{
// resolve callbacks
- m_read_pal_ntsc.resolve_safe(0);
- m_write_prd.resolve_safe();
- m_in_pcb_func.resolve();
- m_in_char_ram_func.resolve();
- m_out_char_ram_func.resolve();
+ m_in_pcb_func.resolve_safe(0);
+ m_in_char_ram_func.resolve_safe(0);
+ m_out_char_ram_func.resolve_safe();
// allocate timers
m_prd_timer = timer_alloc(FUNC(cdp1869_device::prd_update), this);
diff --git a/src/devices/sound/cem3394.cpp b/src/devices/sound/cem3394.cpp
index 0a4ba228827..b047092e00b 100644
--- a/src/devices/sound/cem3394.cpp
+++ b/src/devices/sound/cem3394.cpp
@@ -23,7 +23,11 @@
// logging
-#define LOG_CONTROL_CHANGES (0)
+#define LOG_CONTROL_CHANGES (1U << 1)
+#define LOG_NANS (1U << 2)
+#define LOG_VALUES (1U << 3)
+#define VERBOSE (LOG_NANS)
+#include "logmacro.h"
// use 0.25 as the base volume for pulses
@@ -200,7 +204,7 @@ double cem3394_device::filter(double input, double cutoff)
// catch any NaNs
if (std::isnan(output))
{
- logerror("NAN - vco: %6.0f cutoff: %6.0f res: %.5f output: %.5f\n", m_vco_step / m_inv_sample_rate, cutoff, m_filter_resonance, output);
+ LOGMASKED(LOG_NANS, "NAN - vco: %6.0f cutoff: %6.0f res: %.5f output: %.5f\n", m_vco_step / m_inv_sample_rate, cutoff, m_filter_resonance, output);
output = 0;
m_filter_out[0] = m_filter_out[1] = 0;
}
@@ -253,7 +257,7 @@ double cem3394_device::filter(double input, double cutoff)
// catch NaNs
if (std::isnan(output))
{
- logerror("NAN - vco: %6.0f cutoff: %6.0f res: %.5f output: %.5f\n", m_vco_step / m_inv_sample_rate, cutoff, m_filter_resonance, output);
+ LOGMASKED(LOG_NANS, "NAN - vco: %6.0f cutoff: %6.0f res: %.5f output: %.5f\n", m_vco_step / m_inv_sample_rate, cutoff, m_filter_resonance, output);
output = 0;
}
@@ -298,7 +302,7 @@ void cem3394_device::sound_stream_update(sound_stream &stream, std::vector<read_
auto &buffer = outputs[0];
if (m_wave_select == 0 && m_mixer_external == 0)
- logerror("%f V didn't cut it\n", m_values[WAVE_SELECT]);
+ LOGMASKED(LOG_VALUES, "%f V didn't cut it\n", m_values[WAVE_SELECT]);
// loop over samples
for (int sampindex = 0; sampindex < buffer.samples(); sampindex++)
@@ -452,7 +456,7 @@ void cem3394_device::set_voltage(int input, double voltage)
case VCO_FREQUENCY:
temp = m_vco_zero_freq * pow(2.0, -voltage * (1.0 / 0.75));
m_vco_step = temp * m_inv_sample_rate;
- if (LOG_CONTROL_CHANGES) logerror("VCO_FREQ=%6.3fV -> freq=%f\n", voltage, temp);
+ LOGMASKED(LOG_CONTROL_CHANGES, "VCO_FREQ=%6.3fV -> freq=%f\n", voltage, temp);
break;
// wave select determines triangle/sawtooth enable
@@ -464,7 +468,7 @@ void cem3394_device::set_voltage(int input, double voltage)
m_wave_select |= WAVE_TRIANGLE | WAVE_SAWTOOTH;
else if (voltage >= 2.3 && voltage <= 3.9)
m_wave_select |= WAVE_SAWTOOTH;
- if (LOG_CONTROL_CHANGES) logerror("WAVE_SEL=%6.3fV -> tri=%d saw=%d\n", voltage, (m_wave_select & WAVE_TRIANGLE) ? 1 : 0, (m_wave_select & WAVE_SAWTOOTH) ? 1 : 0);
+ LOGMASKED(LOG_CONTROL_CHANGES, "WAVE_SEL=%6.3fV -> tri=%d saw=%d\n", voltage, (m_wave_select & WAVE_TRIANGLE) ? 1 : 0, (m_wave_select & WAVE_SAWTOOTH) ? 1 : 0);
break;
// pulse width determines duty cycle; 0.0 means 0%, 2.0 means 100%
@@ -481,13 +485,13 @@ void cem3394_device::set_voltage(int input, double voltage)
m_pulse_width = MINIMUM_WIDTH + (MAXIMUM_WIDTH - MINIMUM_WIDTH) * m_pulse_width;
m_wave_select |= WAVE_PULSE;
}
- if (LOG_CONTROL_CHANGES) logerror("PULSE_WI=%6.3fV -> raw=%f adj=%f\n", voltage, voltage * 0.5, m_pulse_width);
+ LOGMASKED(LOG_CONTROL_CHANGES, "PULSE_WI=%6.3fV -> raw=%f adj=%f\n", voltage, voltage * 0.5, m_pulse_width);
break;
// final gain is pretty self-explanatory; 0.0 means ~90dB, 4.0 means 0dB
case FINAL_GAIN:
m_volume = compute_db_volume(voltage);
- if (LOG_CONTROL_CHANGES) logerror("TOT_GAIN=%6.3fV -> vol=%f\n", voltage, m_volume);
+ LOGMASKED(LOG_CONTROL_CHANGES, "TOT_GAIN=%6.3fV -> vol=%f\n", voltage, m_volume);
break;
// mixer balance is a pan between the external input and the internal input
@@ -503,13 +507,13 @@ void cem3394_device::set_voltage(int input, double voltage)
m_mixer_internal = compute_db_volume(3.55 - 0.45 * (voltage * 0.25));
m_mixer_external = compute_db_volume(3.55 + voltage);
}
- if (LOG_CONTROL_CHANGES) logerror(" BALANCE=%6.3fV -> int=%f ext=%f\n", voltage, m_mixer_internal, m_mixer_external);
+ LOGMASKED(LOG_CONTROL_CHANGES, " BALANCE=%6.3fV -> int=%f ext=%f\n", voltage, m_mixer_internal, m_mixer_external);
break;
// filter frequency varies from -3.0 to +4.0, at 0.375V/octave
case FILTER_FREQENCY:
m_filter_frequency = m_filter_zero_freq * pow(2.0, -voltage * (1.0 / 0.375));
- if (LOG_CONTROL_CHANGES) logerror("FLT_FREQ=%6.3fV -> freq=%f\n", voltage, m_filter_frequency);
+ LOGMASKED(LOG_CONTROL_CHANGES, "FLT_FREQ=%6.3fV -> freq=%f\n", voltage, m_filter_frequency);
break;
// modulation depth is 0.01*freq at 0V and 2.0*freq at 3.5V
@@ -520,7 +524,7 @@ void cem3394_device::set_voltage(int input, double voltage)
m_filter_modulation = 1.99;
else
m_filter_modulation = (voltage * (1.0 / 3.5)) * 1.98 + 0.01;
- if (LOG_CONTROL_CHANGES) logerror("FLT_MODU=%6.3fV -> mod=%f\n", voltage, m_filter_modulation);
+ LOGMASKED(LOG_CONTROL_CHANGES, "FLT_MODU=%6.3fV -> mod=%f\n", voltage, m_filter_modulation);
break;
// this is not yet implemented
@@ -531,7 +535,7 @@ void cem3394_device::set_voltage(int input, double voltage)
m_filter_resonance = 1.0;
else
m_filter_resonance = voltage * (1.0 / 2.5);
- if (LOG_CONTROL_CHANGES) logerror("FLT_RESO=%6.3fV -> mod=%f\n", voltage, m_filter_resonance);
+ LOGMASKED(LOG_CONTROL_CHANGES, "FLT_RESO=%6.3fV -> mod=%f\n", voltage, m_filter_resonance);
break;
}
}
diff --git a/src/devices/sound/cf61909.cpp b/src/devices/sound/cf61909.cpp
new file mode 100644
index 00000000000..77b8af0b5de
--- /dev/null
+++ b/src/devices/sound/cf61909.cpp
@@ -0,0 +1,161 @@
+// license: BSD-3-Clause
+// copyright-holders: Devin Acker
+
+/***************************************************************************
+ Texas Instruments CF61909 "DEVO"
+
+ This is the sound and mapper ASIC used in the Jaminator.
+ It generates 8 channels of PCM at ~44.5 kHz, and also handles all
+ ROM access and clock generation for the 8039 MCU.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "cf61909.h"
+
+#include <algorithm>
+
+DEFINE_DEVICE_TYPE(CF61909, cf61909_device, "cf61909", "Texas Instruments CF61909 (DEVO)")
+
+cf61909_device::cf61909_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, CF61909, tag, owner, clock)
+ , device_sound_interface(mconfig, *this)
+ , device_rom_interface(mconfig, *this)
+ , m_sample_clock(*this, "sample_clock")
+{
+}
+
+/**************************************************************************/
+void cf61909_device::device_add_mconfig(machine_config &config)
+{
+ // DEVO outputs a brief low pulse every 4 samples (~11.127 kHz), which the Jaminator MCU uses
+ // for syncing to the sample rate when updating sound registers
+ CLOCK(config, m_sample_clock, DERIVED_CLOCK(1, CLOCKS_PER_SAMPLE * 4));
+ m_sample_clock->set_duty_cycle(1.0 - (16.0 / CLOCKS_PER_SAMPLE));
+}
+
+/**************************************************************************/
+void cf61909_device::device_start()
+{
+ m_stream = stream_alloc(0, 1, clock() / CLOCKS_PER_SAMPLE);
+
+ save_item(NAME(m_data_offset));
+
+ save_item(STRUCT_MEMBER(m_voice, m_regs));
+ save_item(STRUCT_MEMBER(m_voice, m_start));
+ save_item(STRUCT_MEMBER(m_voice, m_loop));
+ save_item(STRUCT_MEMBER(m_voice, m_pos));
+ save_item(STRUCT_MEMBER(m_voice, m_pitch));
+ save_item(STRUCT_MEMBER(m_voice, m_pitch_counter));
+ save_item(STRUCT_MEMBER(m_voice, m_volume));
+}
+
+/**************************************************************************/
+void cf61909_device::device_reset()
+{
+ std::fill(m_voice.begin(), m_voice.end(), voice_t());
+
+ m_data_offset = 0;
+}
+
+/**************************************************************************/
+void cf61909_device::device_clock_changed()
+{
+ m_stream->set_sample_rate(clock() / CLOCKS_PER_SAMPLE);
+}
+
+/**************************************************************************/
+void cf61909_device::rom_bank_pre_change()
+{
+ m_stream->update();
+}
+
+/**************************************************************************/
+u8 cf61909_device::read(offs_t offset)
+{
+ return read_byte(m_data_offset | (offset & 0xff));
+}
+
+/**************************************************************************/
+void cf61909_device::write(offs_t offset, u8 data)
+{
+ voice_t &voice = m_voice[BIT(offset, 4, 3)];
+ const u8 reg = offset & 0xf;
+
+ m_stream->update();
+ voice.m_regs[reg] = data;
+
+ switch (reg)
+ {
+ case 0x1: // position lsb
+ voice.m_pos = (voice.m_regs[0x2] << 8) | data;
+ break;
+
+ case 0x2: // pitch / position msb
+ break;
+
+ case 0x3: // pitch lsb
+ voice.m_pitch = (voice.m_regs[0x2] << 8) | data;
+ break;
+
+ case 0x4: // volume low nibble
+ voice.m_volume = (voice.m_regs[0xc] << 4) | (data & 0xf);
+ break;
+
+ case 0x5: // program bank (TODO)
+ break;
+
+ case 0x6: // data bank
+ m_data_offset = (data & 0x7f) << 8;
+ if (BIT(data, 7))
+ m_data_offset |= 0x20000; // cartridge memory
+ break;
+
+ case 0x9: // sample start lsb
+ voice.m_start = (voice.m_regs[0xa] << 10) | (data << 2);
+ break;
+
+ case 0xa: // sample start / loop msb
+ break;
+
+ case 0xb: // sample loop lsb
+ voice.m_loop = (voice.m_regs[0xa] << 8) | data;
+ break;
+
+ case 0xc: // volume high nibble
+ break;
+
+ default:
+ logerror("%s: unknown register write %02x = %02x\n", machine().describe_context(), offset & 0xff, data);
+ break;
+ }
+}
+
+/**************************************************************************/
+void cf61909_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
+{
+ for (int i = 0; i < outputs[0].samples(); i++)
+ {
+ s32 sample = 0;
+
+ for (voice_t &voice : m_voice)
+ {
+ if (!voice.m_pitch) continue;
+
+ s16 data = read_byte(voice.m_start + voice.m_pos);
+ if (!data)
+ {
+ voice.m_pos += voice.m_loop;
+ data = read_byte(voice.m_start + voice.m_pos);
+ }
+ sample += (data - 0x80) * voice.m_volume;
+
+ voice.m_pitch_counter += voice.m_pitch;
+ voice.m_pos += (voice.m_pitch_counter >> 14);
+ voice.m_pitch_counter &= 0x3fff;
+ }
+
+ // Jaminator patent shows 10-bit sampling, assume that's actually true
+ outputs[0].put_int_clamp(i, sample >> 9, 1 << 9);
+ }
+}
diff --git a/src/devices/sound/cf61909.h b/src/devices/sound/cf61909.h
new file mode 100644
index 00000000000..dbb165ac27e
--- /dev/null
+++ b/src/devices/sound/cf61909.h
@@ -0,0 +1,63 @@
+// license: BSD-3-Clause
+// copyright-holders: Devin Acker
+
+#ifndef MAME_SOUND_CF61909_H
+#define MAME_SOUND_CF61909_H
+
+#pragma once
+
+#include "machine/clock.h"
+#include "dirom.h"
+
+#include <array>
+
+
+class cf61909_device : public device_t, public device_sound_interface, public device_rom_interface<18>
+{
+public:
+ cf61909_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0);
+
+ u8 read(offs_t offset);
+ void write(offs_t offset, u8 data);
+
+ int sync_r() { return m_sample_clock->signal_r(); }
+
+protected:
+ virtual void device_add_mconfig(machine_config &config) override;
+
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_clock_changed() override;
+
+ virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
+ virtual void rom_bank_pre_change() override;
+
+private:
+ /*
+ * Jaminator patent specifies 11.127 kHz sample rate, but the real thing sounds like it has 4x
+ * oversampling. The sync clock output (m_sample_clock) seems to fluctuate quite a bit, but
+ * 11.127 kHz is pretty close to average.
+ */
+ static constexpr unsigned CLOCKS_PER_SAMPLE = 247; // based on 11 MHz clock
+
+ struct voice_t
+ {
+ u8 m_regs[16] = {0};
+ u32 m_start = 0;
+ u16 m_loop = 0;
+ u16 m_pos = 0;
+ u16 m_pitch = 0;
+ u16 m_pitch_counter = 0;
+ u8 m_volume = 0;
+ };
+ std::array<voice_t, 8> m_voice;
+
+ u32 m_data_offset;
+
+ required_device<clock_device> m_sample_clock;
+ sound_stream *m_stream;
+};
+
+DECLARE_DEVICE_TYPE(CF61909, cf61909_device)
+
+#endif // MAME_SOUND_CF61909_H
diff --git a/src/devices/sound/dac.h b/src/devices/sound/dac.h
index e0b4fb01cf9..158cbaa6967 100644
--- a/src/devices/sound/dac.h
+++ b/src/devices/sound/dac.h
@@ -51,7 +51,7 @@ stream_buffer::sample_t dac_mapper_ones_complement(u32 input, u8 bits);
class dac_bit_interface
{
public:
- virtual DECLARE_WRITE_LINE_MEMBER(write) = 0;
+ virtual void write(int state) = 0;
virtual void data_w(u8 data) = 0;
};
@@ -134,7 +134,7 @@ protected:
}
public:
- virtual WRITE_LINE_MEMBER(write) override { this->set_value(state); }
+ virtual void write(int state) override { this->set_value(state); }
virtual void data_w(u8 data) override { this->set_value(data); }
};
@@ -195,6 +195,7 @@ DAC_GENERATOR_EPILOG(_dac_type, _dac_class, _dac_description, _dac_shortname)
DAC_GENERATOR(AD557, ad557_device, dac_byte_device_base, dac_mapper_unsigned, 8, dac_gain_r2r, "AD557 DAC", "ad557")
DAC_GENERATOR(AD558, ad558_device, dac_byte_device_base, dac_mapper_unsigned, 8, dac_gain_r2r, "AD558 DAC", "ad558")
DAC_GENERATOR(AD7224, ad7224_device, dac_byte_device_base, dac_mapper_unsigned, 8, dac_gain_r2r, "AD7224 DAC", "ad7224")
+DAC_GENERATOR(AD7520, ad7520_device, dac_word_device_base, dac_mapper_unsigned, 10, dac_gain_r2r, "AD7520 DAC", "ad7520")
DAC_GENERATOR(AD7521, ad7521_device, dac_word_device_base, dac_mapper_unsigned, 12, dac_gain_r2r, "AD7521 DAC", "ad7521")
DAC_GENERATOR(AD7523, ad7523_device, dac_byte_device_base, dac_mapper_unsigned, 8, dac_gain_r2r, "AD7523 DAC", "ad7523")
DAC_GENERATOR(AD7524, ad7524_device, dac_byte_device_base, dac_mapper_unsigned, 8, dac_gain_r2r, "AD7524 DAC", "ad7524")
@@ -223,8 +224,11 @@ DAC_GENERATOR(DAC_2BIT_BINARY_WEIGHTED, dac_2bit_binary_weighted_device,
DAC_GENERATOR(DAC_2BIT_R2R, dac_2bit_r2r_device, dac_byte_device_base, dac_mapper_unsigned, 2, dac_gain_r2r, "2-Bit R-2R DAC", "dac_2bit_r2r")
DAC_GENERATOR(DAC_2BIT_ONES_COMPLEMENT, dac_2bit_ones_complement_device, dac_byte_device_base, dac_mapper_ones_complement, 2, 1.0, "2-Bit Ones Complement DAC", "dac_2bit_oc")
DAC_GENERATOR(DAC_3BIT_BINARY_WEIGHTED, dac_3bit_binary_weighted_device, dac_byte_device_base, dac_mapper_unsigned, 3, dac_gain_bw, "3-Bit Binary Weighted DAC", "dac_3bit_bw")
+DAC_GENERATOR(DAC_3BIT_R2R, dac_3bit_r2r_device, dac_byte_device_base, dac_mapper_unsigned, 3, dac_gain_r2r, "3-Bit R-2R DAC", "dac_3bit_r2r")
DAC_GENERATOR(DAC_4BIT_BINARY_WEIGHTED, dac_4bit_binary_weighted_device, dac_byte_device_base, dac_mapper_unsigned, 4, dac_gain_bw, "4-Bit Binary Weighted DAC", "dac_4bit_bw")
DAC_GENERATOR(DAC_4BIT_R2R, dac_4bit_r2r_device, dac_byte_device_base, dac_mapper_unsigned, 4, dac_gain_r2r, "4-Bit R-2R DAC", "dac_4bit_r2r")
+DAC_GENERATOR(DAC_5BIT_BINARY_WEIGHTED, dac_5bit_binary_weighted_device, dac_byte_device_base, dac_mapper_unsigned, 5, dac_gain_bw, "5-Bit Binary Weighted DAC", "dac_5bit_bw")
+DAC_GENERATOR(DAC_5BIT_R2R, dac_5bit_r2r_device, dac_byte_device_base, dac_mapper_unsigned, 5, dac_gain_r2r, "5-Bit R-2R DAC", "dac_5bit_r2r")
DAC_GENERATOR(DAC_6BIT_BINARY_WEIGHTED, dac_6bit_binary_weighted_device, dac_byte_device_base, dac_mapper_unsigned, 6, dac_gain_bw, "6-Bit Binary Weighted DAC", "dac_6bit_bw")
DAC_GENERATOR(DAC_6BIT_R2R, dac_6bit_r2r_device, dac_byte_device_base, dac_mapper_unsigned, 6, dac_gain_r2r, "6-Bit R-2R DAC", "dac_6bit_r2r")
DAC_GENERATOR(DAC_8BIT_BINARY_WEIGHTED, dac_8bit_binary_weighted_device, dac_byte_device_base, dac_mapper_unsigned, 8, dac_gain_bw, "8-Bit Binary Weighted DAC", "dac_8bit_bw")
diff --git a/src/devices/sound/dac3350a.cpp b/src/devices/sound/dac3350a.cpp
new file mode 100644
index 00000000000..77e91650a64
--- /dev/null
+++ b/src/devices/sound/dac3350a.cpp
@@ -0,0 +1,332 @@
+// license:BSD-3-Clause
+// copyright-holders:windyfairy
+/*
+ Micronas DAC 3550A Stereo Audio DAC
+ i2c code based on mas3507d
+
+ Accepts 8-bit and 16-bit I2C write operations and has no read operations
+*/
+
+#include "emu.h"
+#include "dac3350a.h"
+
+#define VERBOSE (LOG_GENERAL)
+
+#include "logmacro.h"
+
+enum
+{
+ CMD_DEV_WRITE = 0x9a,
+};
+
+enum
+{
+ IDLE = 0,
+ STARTED,
+ NAK,
+ ACK,
+ ACK2,
+};
+
+enum
+{
+ UNKNOWN = 0,
+ VALIDATED,
+ WRONG,
+};
+
+enum
+{
+ REG_UNKNOWN = 0,
+ REG_SR = 1,
+ REG_AVOL,
+ REG_GCFG,
+};
+
+enum
+{
+ // sample rate control register
+ SR_LR_SEL_BIT = 4, // L/R-bit
+ SR_LR_SEL_LEFT = 0, // (WSI = 0 -> left channel
+ SR_LR_SEL_RIGHT = 1, // (WSI = 0 -> right channel
+
+ SR_SP_SEL_BIT = 3, // delay bit
+ SR_SP_SEL_NO_DELAY = 0,
+ SR_SP_SEL_1_BIT_DELAY = 1,
+
+ SR_SRC_BIT = 0, // 3 bits wide, sample rate control
+ SR_SRC_48 = 0, // 32-48 kHz
+ SR_SRC_32, // 26-32 kHz
+ SR_SRC_24, // 20-26 kHz
+ SR_SRC_16, // 14-20 kHz
+ SR_SRC_12, // 10-14 kHz
+ SR_SRC_8, // 8-10 kHz
+ SR_SRC_A, // autoselect
+};
+
+enum
+{
+ // analog volume register
+ AVOL_DEEM_BIT = 14, // deemphasis on/off
+ AVOL_DEEM_OFF = 0,
+ AVOL_DEEM_ON = 1,
+
+ AVOL_L_BIT = 8, // 6 bits wide, analog audio volume level left
+ AVOL_R_BIT = 0, // 6 bits wide, analog audio volume level right
+};
+
+enum
+{
+ // global configuration register
+ GCFG_SEL_53V_BIT = 6, // select 3V-5V mode
+ GCFG_SEL_53V_3V = 0,
+ GCFG_SEL_53V_5V = 1,
+
+ GCFG_PWMD_BIT = 5, // power-mode
+ GCFG_PWMD_NORMAL = 0,
+ GCFG_PWMD_LOW_POWER = 1,
+
+ GCFG_INSEL_AUX2_BIT = 4, // AUX2 select
+ GCFG_INSEL_AUX2_OFF = 0,
+ GCFG_INSEL_AUX2_ON = 1,
+
+ GCFG_INSEL_AUX1_BIT = 3, // AUX1 select
+ GCFG_INSEL_AUX1_OFF = 0,
+ GCFG_INSEL_AUX1_ON = 1,
+
+ GCFG_INSEL_DAC_BIT = 2, // DAC select
+ GCFG_INSEL_DAC_OFF = 0,
+ GCFG_INSEL_DAC_ON = 1,
+
+ GCFG_AUX_MS_BIT = 1, // aux-mono/stereo
+ GCFG_AUX_MS_STEREO = 0,
+ GCFG_AUX_MS_MONO = 1,
+
+ GCFG_IPRA_BIT = 0, // invert right power amplifier
+ GCFG_IPRA_NOT_INVERTED = 0,
+ GCFG_IPRA_INVERTED = 1,
+};
+
+
+DEFINE_DEVICE_TYPE(DAC3350A, dac3350a_device, "dac3350a", "Micronas DAC 3550A Stereo Audio DAC")
+
+dac3350a_device::dac3350a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, DAC3350A, tag, owner, clock)
+ , device_sound_interface(mconfig, *this)
+{
+}
+
+void dac3350a_device::device_start()
+{
+ // TODO: use configured clock for sample rate and respond to device_clock_changed
+ m_stream = stream_alloc(2, 2, 44100);
+
+ save_item(NAME(m_i2c_bus_state));
+ save_item(NAME(m_i2c_bus_address));
+ save_item(NAME(m_i2c_scli));
+ save_item(NAME(m_i2c_sdai));
+ save_item(NAME(m_i2c_bus_curbit));
+ save_item(NAME(m_i2c_bus_curval));
+ save_item(NAME(m_i2c_bytecount));
+
+ save_item(NAME(m_i2c_subadr));
+ save_item(NAME(m_i2c_data));
+
+ save_item(NAME(m_dac_enable));
+ save_item(NAME(m_volume));
+}
+
+void dac3350a_device::device_reset()
+{
+ m_i2c_scli = m_i2c_sdai = 1;
+ m_i2c_bus_state = IDLE;
+ m_i2c_bus_address = UNKNOWN;
+ m_i2c_bus_curbit = -1;
+ m_i2c_bus_curval = 0;
+
+ m_i2c_subadr = 0;
+ m_i2c_data = 0;
+
+ m_dac_enable = true;
+
+ std::fill(std::begin(m_volume), std::end(m_volume), 1.0);
+}
+
+void dac3350a_device::i2c_scl_w(int line)
+{
+ if (line == m_i2c_scli)
+ return;
+
+ m_i2c_scli = line;
+
+ if (m_i2c_scli)
+ {
+ if (m_i2c_bus_state == STARTED)
+ {
+ m_i2c_bus_curval |= m_i2c_sdai << m_i2c_bus_curbit;
+ m_i2c_bus_curbit--;
+
+ if (m_i2c_bus_curbit == -1)
+ {
+ if (m_i2c_bus_address == UNKNOWN)
+ {
+ if (m_i2c_bus_curval == CMD_DEV_WRITE)
+ {
+ m_i2c_bus_state = ACK;
+ m_i2c_bus_address = VALIDATED;
+ m_i2c_bus_curval = 0;
+ }
+ else
+ {
+ m_i2c_bus_state = NAK;
+ m_i2c_bus_address = WRONG;
+ }
+ }
+ else if (m_i2c_bus_address == VALIDATED)
+ {
+ if (m_i2c_bytecount < 3)
+ {
+ m_i2c_bus_state = ACK;
+
+ if (m_i2c_bytecount == 0)
+ {
+ m_i2c_subadr = m_i2c_bus_curval;
+ }
+ else
+ {
+ m_i2c_data <<= 8;
+ m_i2c_data |= m_i2c_bus_curval;
+ }
+
+ m_i2c_bytecount++;
+ }
+ else
+ {
+ // Only accept 8-bit and 16-bit writes
+ m_i2c_bus_state = NAK;
+ }
+ }
+ }
+ }
+ else if (m_i2c_bus_state == ACK)
+ {
+ m_i2c_bus_state = ACK2;
+ }
+ }
+ else
+ {
+ if (m_i2c_bus_state == ACK2)
+ {
+ m_i2c_bus_state = STARTED;
+ m_i2c_bus_curbit = 7;
+ m_i2c_bus_curval = 0;
+ }
+ }
+}
+
+void dac3350a_device::i2c_sda_w(int line)
+{
+ if (line == m_i2c_sdai)
+ return;
+
+ m_i2c_sdai = line;
+
+ if (m_i2c_scli)
+ {
+ if (m_i2c_sdai)
+ i2c_device_handle_write();
+
+ m_i2c_bus_state = m_i2c_sdai ? IDLE : STARTED;
+ m_i2c_bus_address = UNKNOWN;
+ m_i2c_bus_curbit = 7;
+ m_i2c_bus_curval = 0;
+ }
+}
+
+void dac3350a_device::i2c_device_handle_write()
+{
+ const auto bytecount = m_i2c_bytecount;
+
+ m_i2c_bytecount = 0;
+
+ if (bytecount < 2) // not a valid command, ignore
+ return;
+
+ // const int mcs = BIT(m_i2c_subadr, 6, 2); // chip select, 3 = MPEG mode
+ const int reg_adr = BIT(m_i2c_subadr, 0, 2);
+
+ if (reg_adr == REG_SR)
+ {
+ // sample rate control
+ const int sample_rate = BIT(m_i2c_data, SR_SRC_BIT, 3);
+ const int delay = BIT(m_i2c_data, SR_SP_SEL_BIT);
+ const int lr = BIT(m_i2c_data, SR_LR_SEL_BIT);
+
+ LOG("DAC: SR register %d %d %d\n", sample_rate, delay, lr);
+ }
+ else if (reg_adr == REG_AVOL)
+ {
+ // set volume, requires 2 bytes
+ if (bytecount <= 2)
+ logerror("AVOL command requires 2 bytes of data, found %d\n", bytecount);
+
+ const int vol_r = BIT(m_i2c_data, AVOL_R_BIT, 6);
+ const int vol_l = BIT(m_i2c_data, AVOL_L_BIT, 6);
+ const int deemph = BIT(m_i2c_data, AVOL_DEEM_BIT);
+
+ m_volume[0] = calculate_volume(vol_l);
+ m_volume[1] = calculate_volume(vol_r);
+
+ LOG("DAC: AVOL register %d %d %d (%f %f)\n", vol_r, vol_l, deemph, m_volume[0], m_volume[1]);
+ }
+ else if (reg_adr == REG_GCFG)
+ {
+ // global configuration
+ const int ipra = BIT(m_i2c_data, GCFG_IPRA_BIT);
+ const int aux_ms = BIT(m_i2c_data, GCFG_AUX_MS_BIT);
+ const int insel_dac = BIT(m_i2c_data, GCFG_INSEL_DAC_BIT);
+ const int insel_aux1 = BIT(m_i2c_data, GCFG_INSEL_AUX1_BIT);
+ const int insel_aux2 = BIT(m_i2c_data, GCFG_INSEL_AUX2_BIT);
+ const int pwmd = BIT(m_i2c_data, GCFG_PWMD_BIT);
+ const int sel_53v = BIT(m_i2c_data, GCFG_SEL_53V_BIT);
+
+ LOG("DAC: GCFG register %d %d %d %d %d %d %d\n", ipra, aux_ms, insel_dac, insel_aux1, insel_aux2, pwmd, sel_53v);
+ }
+ else
+ {
+ LOG("DAC: Unknown register selected! %d\n", reg_adr);
+ }
+}
+
+float dac3350a_device::calculate_volume(int val)
+{
+ if (val == 0)
+ return 0.0; // mute
+
+ double db = 0;
+ if (val <= 7)
+ {
+ // -75 dB ... -54 dB: 3 dB steps
+ db = -54 - ((8 - val) * 3);
+ }
+ else
+ {
+ // -54 dB ... +18 dB: 1.5 dB steps
+ db = -54 + ((val - 8) * 1.5);
+ }
+
+ db = std::clamp(db, -75.0, 18.0); // range is +18 dB to -75 dB
+
+ return powf(10.0, db / 20.0);
+}
+
+void dac3350a_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
+{
+ const stream_buffer::sample_t enable_scale = m_dac_enable ? 1.0 : 0.0;
+
+ for (int channel = 0; channel < 2 && channel < outputs.size(); channel++)
+ {
+ for (int sampindex = 0; sampindex < outputs[channel].samples(); sampindex++)
+ outputs[channel].put(sampindex, inputs[channel].get(sampindex) * enable_scale * m_volume[channel]);
+ }
+}
diff --git a/src/devices/sound/dac3350a.h b/src/devices/sound/dac3350a.h
new file mode 100644
index 00000000000..59e5bc90ea7
--- /dev/null
+++ b/src/devices/sound/dac3350a.h
@@ -0,0 +1,46 @@
+// license:BSD-3-Clause
+// copyright-holders:windyfairy
+#ifndef MAME_SOUND_DAC3350A_H
+#define MAME_SOUND_DAC3350A_H
+
+#pragma once
+
+class dac3350a_device : public device_t, public device_sound_interface
+{
+public:
+ dac3350a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
+
+ void i2c_scl_w(int line);
+ void i2c_sda_w(int line);
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
+
+private:
+ void i2c_device_handle_write();
+ float calculate_volume(int val);
+
+ sound_stream *m_stream;
+
+ uint8_t m_i2c_bus_state;
+ uint8_t m_i2c_bus_address;
+
+ uint8_t m_i2c_scli, m_i2c_sdai;
+ int32_t m_i2c_bus_curbit;
+ uint8_t m_i2c_bus_curval;
+ uint32_t m_i2c_bytecount;
+
+ uint8_t m_i2c_subadr;
+ uint16_t m_i2c_data;
+
+ bool m_dac_enable;
+
+ float m_volume[2];
+};
+
+DECLARE_DEVICE_TYPE(DAC3350A, dac3350a_device)
+
+#endif // MAME_SOUND_DAC3350A_H
diff --git a/src/devices/sound/dac76.h b/src/devices/sound/dac76.h
index 7f3cd1a52fd..4c42d72961c 100644
--- a/src/devices/sound/dac76.h
+++ b/src/devices/sound/dac76.h
@@ -37,18 +37,18 @@ public:
dac76_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
// chord
- DECLARE_WRITE_LINE_MEMBER(b1_w) { m_chord &= ~(1 << 2); m_chord |= (state << 2); }
- DECLARE_WRITE_LINE_MEMBER(b2_w) { m_chord &= ~(1 << 1); m_chord |= (state << 1); }
- DECLARE_WRITE_LINE_MEMBER(b3_w) { m_chord &= ~(1 << 0); m_chord |= (state << 0); }
+ void b1_w(int state) { m_chord &= ~(1 << 2); m_chord |= (state << 2); }
+ void b2_w(int state) { m_chord &= ~(1 << 1); m_chord |= (state << 1); }
+ void b3_w(int state) { m_chord &= ~(1 << 0); m_chord |= (state << 0); }
// step
- DECLARE_WRITE_LINE_MEMBER(b4_w) { m_step &= ~(1 << 3); m_step |= (state << 3); }
- DECLARE_WRITE_LINE_MEMBER(b5_w) { m_step &= ~(1 << 2); m_step |= (state << 2); }
- DECLARE_WRITE_LINE_MEMBER(b6_w) { m_step &= ~(1 << 1); m_step |= (state << 1); }
- DECLARE_WRITE_LINE_MEMBER(b7_w) { m_step &= ~(1 << 0); m_step |= (state << 0); }
+ void b4_w(int state) { m_step &= ~(1 << 3); m_step |= (state << 3); }
+ void b5_w(int state) { m_step &= ~(1 << 2); m_step |= (state << 2); }
+ void b6_w(int state) { m_step &= ~(1 << 1); m_step |= (state << 1); }
+ void b7_w(int state) { m_step &= ~(1 << 0); m_step |= (state << 0); }
// sign bit
- DECLARE_WRITE_LINE_MEMBER(sb_w) { m_sb = bool(state); }
+ void sb_w(int state) { m_sb = bool(state); }
void update() { m_stream->update(); }
@@ -64,8 +64,8 @@ private:
sound_stream *m_stream;
- uint8_t m_chord; // 4-bit
- uint8_t m_step; // 3-bit
+ uint8_t m_chord; // 3-bit
+ uint8_t m_step; // 4-bit
bool m_sb;
};
diff --git a/src/devices/sound/dave.cpp b/src/devices/sound/dave.cpp
index 44719ab4584..1d2a153a214 100644
--- a/src/devices/sound/dave.cpp
+++ b/src/devices/sound/dave.cpp
@@ -77,11 +77,6 @@ dave_device::dave_device(const machine_config &mconfig, const char *tag, device_
void dave_device::device_start()
{
- // resolve callbacks
- m_write_irq.resolve_safe();
- m_write_lh.resolve_safe();
- m_write_rh.resolve_safe();
-
// allocate timers
m_timer_1hz = timer_alloc(FUNC(dave_device::update_1hz_timer), this);
m_timer_1hz->adjust(attotime::from_hz(2), 0, attotime::from_hz(2));
@@ -276,7 +271,7 @@ void dave_device::sound_stream_update(sound_stream &stream, std::vector<read_str
// int1_w - interrupt 1 write
//-------------------------------------------------
-WRITE_LINE_MEMBER( dave_device::int1_w )
+void dave_device::int1_w(int state)
{
if (!(m_irq_status & IRQ_INT1) && state)
m_irq_status |= IRQ_INT1_LATCH;
@@ -294,7 +289,7 @@ WRITE_LINE_MEMBER( dave_device::int1_w )
// int2_w - interrupt 2 write
//-------------------------------------------------
-WRITE_LINE_MEMBER( dave_device::int2_w )
+void dave_device::int2_w(int state)
{
if (!(m_irq_status & IRQ_INT2) && state)
m_irq_status |= IRQ_INT2_LATCH;
diff --git a/src/devices/sound/dave.h b/src/devices/sound/dave.h
index 82c024f748e..1bbba67d8b1 100644
--- a/src/devices/sound/dave.h
+++ b/src/devices/sound/dave.h
@@ -32,8 +32,8 @@ public:
virtual void z80_program_map(address_map &map);
virtual void z80_io_map(address_map &map);
- DECLARE_WRITE_LINE_MEMBER( int1_w );
- DECLARE_WRITE_LINE_MEMBER( int2_w );
+ void int1_w(int state);
+ void int2_w(int state);
void io_map(address_map &map);
void program_map(address_map &map);
diff --git a/src/devices/sound/discrete.cpp b/src/devices/sound/discrete.cpp
index 0ad9f34562a..ad5b89c8bd9 100644
--- a/src/devices/sound/discrete.cpp
+++ b/src/devices/sound/discrete.cpp
@@ -17,7 +17,7 @@
*
***********************************************************************
*
- * Each sound primative DSS_xxxx or DST_xxxx has its own implementation
+ * Each sound primitive DSS_xxxx or DST_xxxx has its own implementation
* file. All discrete sound primatives MUST implement the following
* API:
*
@@ -41,6 +41,7 @@
#include "wavwrite.h"
#include <atomic>
+#include <cstdarg>
#include <iostream>
@@ -64,7 +65,7 @@ DEFINE_DEVICE_TYPE(DISCRETE, discrete_sound_device, "discrete", "Discrete Sound"
* Values > 500 have a slightly worse performace (too much cache misses?).
*/
-#define MAX_SAMPLES_PER_TASK_SLICE (960/4)
+static constexpr int MAX_SAMPLES_PER_TASK_SLICE = 960 / 4;
/*************************************
*
@@ -90,61 +91,70 @@ DEFINE_DEVICE_TYPE(DISCRETE, discrete_sound_device, "discrete", "Discrete Sound"
struct output_buffer
{
+ output_buffer() : source(nullptr), ptr(nullptr)
+ {
+ }
+
+ // required for use in vector (std::atomic<T> has deleted copy constructor)
+ output_buffer(output_buffer &&that) : node_buf(std::move(that.node_buf)), source(that.source), ptr(that.ptr.load(std::memory_order_relaxed)), node_num(that.node_num)
+ {
+ }
+
std::unique_ptr<double []> node_buf;
- const double *source;
- volatile double *ptr;
+ const double * source;
+ std::atomic<double *> ptr;
int node_num;
};
struct input_buffer
{
- volatile const double *ptr; /* pointer into linked_outbuf.nodebuf */
- output_buffer * linked_outbuf; /* what output are we connected to ? */
- double buffer; /* input[] will point here */
+ const double * ptr; // pointer into linked_outbuf.nodebuf
+ output_buffer * linked_outbuf; // what output are we connected to?
+ double buffer; // input[] will point here
};
class discrete_task
{
- friend class discrete_device;
public:
- virtual ~discrete_task() { }
-
- inline void step_nodes();
- inline bool lock_threadid(int32_t threadid)
+ discrete_task(discrete_device &pdev) : m_device(pdev), m_threadid(-1), m_samples(0)
{
- int expected = -1;
- return m_threadid.compare_exchange_weak(expected, threadid, std::memory_order_release,std::memory_order_relaxed);
+ // FIXME: the code expects to be able to take pointers to members of elements of this vector before it's filled
+ source_list.reserve(16);
}
- inline void unlock() { m_threadid = -1; }
+
+ void check(discrete_task &dest_task);
+ void prepare_for_queue(int samples);
+
+ static void *task_callback(void *param, int threadid);
//const linked_list_entry *list;
discrete_device::node_step_list_t step_list;
- /* list of source nodes */
- std::vector<input_buffer> source_list; /* discrete_source_node */
-
int task_group = 0;
+private:
+ void step_nodes();
+ bool process();
- discrete_task(discrete_device &pdev) : m_device(pdev), m_threadid(-1)
+ bool lock_threadid(int32_t threadid)
{
- // FIXME: the code expects to be able to take pointers to members of elements of this vector before it's filled
- source_list.reserve(16);
+ int expected = -1;
+ return m_threadid.compare_exchange_strong(expected, threadid, std::memory_order_acquire, std::memory_order_relaxed);
}
-protected:
- static void *task_callback(void *param, int threadid);
- inline bool process();
+ void unlock()
+ {
+ m_threadid.store(-1, std::memory_order_release);
+ }
- void check(discrete_task &dest_task);
- void prepare_for_queue(int samples);
+ /* list of source nodes */
+ std::vector<input_buffer> source_list; /* discrete_source_node */
- std::vector<output_buffer> m_buffers;
- discrete_device & m_device;
+ std::vector<output_buffer> m_buffers;
+ discrete_device & m_device;
-private:
- std::atomic<int32_t> m_threadid;
- volatile int m_samples = 0;
+ std::atomic<int32_t> m_threadid;
+ int m_samples;
};
@@ -178,17 +188,13 @@ private:
inline void discrete_task::step_nodes()
{
for (input_buffer &sn : source_list)
- {
sn.buffer = *sn.ptr++;
- }
if (EXPECTED(!m_device.profiling()))
{
+ // Now step the nodes
for (discrete_step_interface *entry : step_list)
- {
- /* Now step the node */
entry->step();
- }
}
else
{
@@ -203,19 +209,22 @@ inline void discrete_task::step_nodes()
}
}
- /* buffer the outputs */
+ // buffer the outputs
+ for (output_buffer &outbuf : m_buffers)
+ *outbuf.ptr.load(std::memory_order_relaxed) = *outbuf.source;
+ std::atomic_thread_fence(std::memory_order_release);
for (output_buffer &outbuf : m_buffers)
- *outbuf.ptr++ = *outbuf.source;
+ outbuf.ptr.store(outbuf.ptr.load(std::memory_order_relaxed) + 1, std::memory_order_relaxed);
}
void *discrete_task::task_callback(void *param, int threadid)
{
const auto &list = *reinterpret_cast<const discrete_sound_device::task_list_t *>(param);
- do
+ while (true)
{
for (const auto &task : list)
{
- /* try to lock */
+ // try to lock
if (task->lock_threadid(threadid))
{
if (!task->process())
@@ -223,37 +232,38 @@ void *discrete_task::task_callback(void *param, int threadid)
task->unlock();
}
}
- } while (1);
+ }
return nullptr;
}
-bool discrete_task::process()
+inline bool discrete_task::process()
{
- int samples = std::min(int(m_samples), MAX_SAMPLES_PER_TASK_SLICE);
+ int samples = std::min(m_samples, MAX_SAMPLES_PER_TASK_SLICE);
- /* check dependencies */
+ // check dependencies
for (input_buffer &sn : source_list)
{
- int avail = sn.linked_outbuf->ptr - sn.ptr;
+ const int avail = sn.linked_outbuf->ptr.load(std::memory_order_relaxed) - sn.ptr;
if (avail < 0)
throw emu_fatalerror("discrete_task::process: available samples are negative");
if (avail < samples)
samples = avail;
}
+ std::atomic_thread_fence(std::memory_order_acquire);
m_samples -= samples;
if (m_samples < 0)
throw emu_fatalerror("discrete_task::process: m_samples got negative");
while (samples > 0)
{
- /* step */
+ // step
step_nodes();
samples--;
}
if (m_samples == 0)
{
- /* return and keep the task locked so it is not picked up by other worker threads */
+ // return and keep the task locked so it is not picked up by other worker threads
return false;
}
return true;
@@ -261,16 +271,16 @@ bool discrete_task::process()
void discrete_task::prepare_for_queue(int samples)
{
+ m_threadid.store(-1, std::memory_order_relaxed); // unlock the thread
m_samples = samples;
- /* set up task buffers */
+
+ // set up task buffers
for (output_buffer &ob : m_buffers)
- ob.ptr = ob.node_buf.get();
+ ob.ptr.store(ob.node_buf.get(), std::memory_order_relaxed);
- /* initialize sources */
+ // initialize sources
for (input_buffer &sn : source_list)
- {
sn.ptr = sn.linked_outbuf->node_buf.get();
- }
}
void discrete_task::check(discrete_task &dest_task)
@@ -296,7 +306,7 @@ void discrete_task::check(discrete_task &dest_task)
int inputnode_num = dest_node->input_node(inputnum);
if IS_VALUE_A_NODE(inputnode_num)
{
- /* Fixme: sub nodes ! */
+ /* FIXME: sub nodes ! */
if (NODE_DEFAULT_NODE(task_node->block_node()) == NODE_DEFAULT_NODE(inputnode_num))
{
int found = -1;
@@ -311,16 +321,16 @@ void discrete_task::check(discrete_task &dest_task)
break;
}
- if (found<0)
+ if (found < 0)
{
output_buffer buf;
buf.node_buf = std::make_unique<double []>((task_node->sample_rate() + sound_manager::STREAMS_UPDATE_FREQUENCY) / sound_manager::STREAMS_UPDATE_FREQUENCY);
- buf.ptr = buf.node_buf.get();
+ buf.ptr.store(buf.node_buf.get(), std::memory_order_relaxed);
buf.source = dest_node->m_input[inputnum];
buf.node_num = inputnode_num;
//buf.node = device->discrete_find_node(inputnode);
- m_buffers.push_back(std::move(buf));
+ m_buffers.emplace_back(std::move(buf));
pbuf = &m_buffers.back();
}
m_device.discrete_log("dso_task_start - buffering %d(%d) in task %p group %d referenced by %d group %d", NODE_INDEX(inputnode_num), NODE_CHILD_NODE_NUM(inputnode_num), this, task_group, dest_node->index(), dest_task.task_group);
@@ -329,7 +339,6 @@ void discrete_task::check(discrete_task &dest_task)
dest_task.source_list.push_back(input_buffer{ nullptr, pbuf, 0.0 });
// FIXME: taking address of element of vector before it's filled
dest_node->m_input[inputnum] = &dest_task.source_list.back().buffer;
-
}
}
}
@@ -1016,22 +1025,18 @@ void discrete_device::process(int samples)
if (samples == 0)
return;
- /* Setup tasks */
+ // Set up tasks
for (const auto &task : task_list)
- {
- /* unlock the thread */
- task->unlock();
-
task->prepare_for_queue(samples);
- }
+ std::atomic_thread_fence(std::memory_order_release);
for (const auto &task : task_list)
{
- /* Fire a work item for each task */
+ // Fire a work item for each task
(void)task;
osd_work_item_queue(m_queue, discrete_task::task_callback, (void *)&task_list, WORK_ITEM_FLAG_AUTO_RELEASE);
}
- osd_work_queue_wait(m_queue, osd_ticks_per_second()*10);
+ osd_work_queue_wait(m_queue, osd_ticks_per_second() * 10);
if (m_profiling)
{
diff --git a/src/devices/sound/discrete.h b/src/devices/sound/discrete.h
index eebc4936df7..e70b70e3d1d 100644
--- a/src/devices/sound/discrete.h
+++ b/src/devices/sound/discrete.h
@@ -4195,7 +4195,7 @@ public:
virtual ~discrete_device();
template<int DiscreteInput>
- DECLARE_WRITE_LINE_MEMBER(write_line)
+ void write_line(int state)
{
write(DiscreteInput, state ? 1 : 0);
}
diff --git a/src/devices/sound/dspv.cpp b/src/devices/sound/dspv.cpp
index cf14ead15d3..b0f4d8f3596 100644
--- a/src/devices/sound/dspv.cpp
+++ b/src/devices/sound/dspv.cpp
@@ -6,13 +6,16 @@
#include "emu.h"
#include "dspv.h"
-DEFINE_DEVICE_TYPE(DSPV, dspv_device, "dspv", "Yamaha DSPV audio simulation DSP (YSS217-F/")
+DEFINE_DEVICE_TYPE(DSPV, dspv_device, "dspv", "Yamaha DSPV audio simulation DSP (YSS217-F)")
dspv_device::dspv_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: cpu_device(mconfig, DSPV, tag, owner, clock),
device_sound_interface(mconfig, *this),
- m_program_config("program", ENDIANNESS_BIG, 16, 16, -1, address_map_constructor(FUNC(dspv_device::prg_map), this)),
- m_data_config("data", ENDIANNESS_BIG, 16, 14, -1, address_map_constructor(FUNC(dspv_device::data_map), this))
+ m_prg1_config("prg1", ENDIANNESS_BIG, 64, 8, -3, address_map_constructor(FUNC(dspv_device::prg1_map), this)),
+ m_prg2_config("prg2", ENDIANNESS_BIG, 64, 8, -3, address_map_constructor(FUNC(dspv_device::prg2_map), this)),
+ m_data_config("data", ENDIANNESS_BIG, 16, 32, -1, address_map_constructor(FUNC(dspv_device::data_map), this)),
+ m_prg1(*this, "prg1"),
+ m_prg2(*this, "prg2")
{
}
@@ -22,57 +25,74 @@ void dspv_device::map(address_map &map)
map(0x02, 0x03).r(FUNC(dspv_device::status_r));
map(0x06, 0x07).w(FUNC(dspv_device::prg_adr_w));
- map(0x20, 0x21).w(FUNC(dspv_device::table_adrh_w));
- map(0x22, 0x23).w(FUNC(dspv_device::table_adrl_w));
- map(0x24, 0x25).w(FUNC(dspv_device::table_data_w));
- map(0x26, 0x27).w(FUNC(dspv_device::table_zero_w));
- map(0x40, 0x7f).w(FUNC(dspv_device::prg_data_w));
+ map(0x12, 0x13).lw16(NAME([](u16 data) { }));
+ map(0x20, 0x21).w(FUNC(dspv_device::data_adrh_w));
+ map(0x22, 0x23).w(FUNC(dspv_device::data_adrl_w));
+ map(0x24, 0x25).w(FUNC(dspv_device::data_data_w));
+ map(0x26, 0x27).w(FUNC(dspv_device::data_zero_w));
+ map(0x38, 0x39).lr16(NAME([]() -> u16 { return 0; }));
+ map(0x40, 0x7f).rw(FUNC(dspv_device::prg_data_r), FUNC(dspv_device::prg_data_w));
}
-void dspv_device::prg_map(address_map &map)
+void dspv_device::prg1_map(address_map &map)
{
- map(0x0000, 0xffff).ram();
+ map(0x00, 0xff).ram().share(m_prg1);
+}
+
+void dspv_device::prg2_map(address_map &map)
+{
+ map(0x00, 0xff).ram().share(m_prg2);
}
void dspv_device::data_map(address_map &map)
{
- map(0x0000, 0x3fff).ram();
+ map(0x00000, 0x03fff).ram();
+ map(0x1c000, 0x1dfff).ram();
}
-void dspv_device::table_adrh_w(u16 data)
+void dspv_device::data_adrh_w(u16 data)
{
- m_table_adr = (m_table_adr & 0x0000ffff) | (data << 16);
+ m_data_adr = (m_data_adr & 0x0000ffff) | (data << 16);
}
-void dspv_device::table_adrl_w(u16 data)
+void dspv_device::data_adrl_w(u16 data)
{
- m_table_adr = (m_table_adr & 0xffff0000) | data;
+ m_data_adr = (m_data_adr & 0xffff0000) | data;
}
-void dspv_device::table_data_w(u16 data)
+void dspv_device::data_data_w(u16 data)
{
- if(m_table_adr >= 0x4000)
- logerror("table_adr overflow!\n");
- m_data->write_word(m_table_adr, data);
- m_table_adr++;
+ m_data->write_word(m_data_adr, data);
+ m_data_adr++;
}
-void dspv_device::table_zero_w(u16 data)
+void dspv_device::data_zero_w(u16 data)
{
- if(data)
- logerror("table_zero_w %04x\n", data);
+ logerror("data_zero_w %04x\n", data);
}
void dspv_device::prg_adr_w(u16 data)
{
- m_prg_adr = data;
+ u32 slot = BIT(data, 13, 3);
+ u32 len = BIT(data, 8, 5);
+ u32 address = BIT(data, 0, 8);
+ if(len == 0)
+ len = 0x20;
+ auto &prg = slot >= 4 ? m_prg2 : m_prg1;
+ u32 shift = (slot & 3)*16;
+ u64 mask = ~(u64(0xffff) << shift);
+ for(u32 i=0; i != len; i++)
+ prg[(i + address) & 0xff] = (prg[(i + address) & 0xff] & mask) | (u64(m_buffer[i]) << shift);
}
void dspv_device::prg_data_w(offs_t offset, u16 data)
{
- u16 adr = m_prg_adr + offset;
- adr = (adr << 3) | (adr >> 13);
- m_program->write_word(adr, data);
+ m_buffer[offset] = data;
+}
+
+u16 dspv_device::prg_data_r(offs_t offset)
+{
+ return m_buffer[offset];
}
u16 dspv_device::status_r()
@@ -100,7 +120,6 @@ void dspv_device::sound_stream_update(sound_stream &stream, std::vector<read_str
void dspv_device::device_start()
{
- m_program = &space(AS_PROGRAM);
m_data = &space(AS_DATA);
state_add(STATE_GENPC, "GENPC", m_pc).noshow();
state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow();
@@ -110,16 +129,16 @@ void dspv_device::device_start()
save_item(NAME(m_pc));
save_item(NAME(m_status));
- save_item(NAME(m_table_adr));
- save_item(NAME(m_prg_adr));
+ save_item(NAME(m_data_adr));
+ save_item(NAME(m_buffer));
}
void dspv_device::device_reset()
{
m_pc = 0;
m_status = 0;
- m_table_adr = 0;
- m_prg_adr = 0;
+ m_data_adr = 0;
+ std::fill(m_buffer.begin(), m_buffer.end(), 0);
}
uint32_t dspv_device::execute_min_cycles() const noexcept
@@ -147,7 +166,8 @@ void dspv_device::execute_run()
device_memory_interface::space_config_vector dspv_device::memory_space_config() const
{
return space_config_vector {
- std::make_pair(AS_PROGRAM, &m_program_config),
+ std::make_pair(AS_OPCODES, &m_prg1_config),
+ std::make_pair(AS_PROGRAM, &m_prg2_config),
std::make_pair(AS_DATA, &m_data_config)
};
}
diff --git a/src/devices/sound/dspv.h b/src/devices/sound/dspv.h
index fb68acda569..9be1cb3963d 100644
--- a/src/devices/sound/dspv.h
+++ b/src/devices/sound/dspv.h
@@ -32,25 +32,29 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
private:
- address_space_config m_program_config, m_data_config;
- address_space *m_program, *m_data;
+ address_space_config m_prg1_config, m_prg2_config, m_data_config;
+ address_space *m_data;
+
+ required_shared_ptr<u64> m_prg1, m_prg2;
+
+ std::array<u16, 0x20> m_buffer;
u32 m_pc;
int m_icount;
- u32 m_table_adr;
- u16 m_prg_adr;
+ u32 m_data_adr;
u16 m_status;
- // Table ram access
- void table_adrh_w(u16 data);
- void table_adrl_w(u16 data);
- void table_data_w(u16 data);
- void table_zero_w(u16 data);
+ // Data ram access
+ void data_adrh_w(u16 data);
+ void data_adrl_w(u16 data);
+ void data_data_w(u16 data);
+ void data_zero_w(u16 data);
// Program ram access
void prg_adr_w(u16 data);
void prg_data_w(offs_t offset, u16 data);
+ u16 prg_data_r(offs_t offset);
// Registers
u16 status_r();
@@ -59,7 +63,8 @@ private:
u16 snd_r(offs_t offset);
void snd_w(offs_t offset, u16 data);
- void prg_map(address_map &map);
+ void prg1_map(address_map &map);
+ void prg2_map(address_map &map);
void data_map(address_map &map);
};
diff --git a/src/devices/sound/dspvd.cpp b/src/devices/sound/dspvd.cpp
index 3a2fd254cf6..2865958113a 100644
--- a/src/devices/sound/dspvd.cpp
+++ b/src/devices/sound/dspvd.cpp
@@ -21,9 +21,9 @@ u32 dspv_disassembler::opcode_alignment() const
offs_t dspv_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params)
{
- u16 opc = opcodes.r16(pc);
-
- util::stream_format(stream, "dc.w %04x", opc);
+ u64 opc = opcodes.r64(pc);
+ u64 mode = (params.r64(pc) >> 32) & 7;
+ util::stream_format(stream, "%x%016x", mode, opc);
return 1 | SUPPORTED;
}
diff --git a/src/devices/sound/es1373.cpp b/src/devices/sound/es1373.cpp
index d254527b0bc..5ce753da4a7 100644
--- a/src/devices/sound/es1373.cpp
+++ b/src/devices/sound/es1373.cpp
@@ -6,9 +6,16 @@
#include "speaker.h"
-#define LOG_ES (0)
-#define LOG_ES_REG (0)
-#define LOG_ES_FILE (0)
+#define LOG_OTHER (1U << 1)
+#define LOG_REG (1U << 2)
+#define LOG_IRQ (1U << 3)
+#define LOG_ADC (1U << 4)
+#define LOG_INVALID (1U << 5)
+#define LOG_UNIMPL (1U << 6)
+#define LOG_SAMPLES (1U << 7)
+
+#define VERBOSE (LOG_UNIMPL | LOG_INVALID)
+#include "logmacro.h"
/* Ensonic ES1373 registers 0x00-0x3f */
@@ -87,30 +94,11 @@ void es1373_device::map(address_map &map)
es1373_device::es1373_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: pci_device(mconfig, ES1373, tag, owner, clock)
, device_sound_interface(mconfig, *this), m_stream(nullptr)
- , m_eslog(nullptr), m_tempCount(0), m_timer(nullptr), m_memory_space(nullptr), m_irq_handler(*this)
+ , m_timer(nullptr), m_memory_space(nullptr), m_irq_handler(*this)
{
set_ids(0x12741371, 0x04, 0x040100, 0x12741371);
}
-void es1373_device::device_resolve_objects()
-{
- pci_device::device_resolve_objects();
- m_irq_handler.resolve_safe();
-}
-
-//-------------------------------------------------
-// device_stop - device-specific stop
-//-------------------------------------------------
-void es1373_device::device_stop()
-{
- /* debugging */
- if (LOG_ES_FILE && m_eslog)
- {
- fclose(m_eslog);
- m_eslog = nullptr;
- }
-}
-
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
@@ -179,16 +167,6 @@ void es1373_device::device_post_load()
void es1373_device::device_reset()
{
- // debugging
- m_tempCount = 0;
- if (LOG_ES_FILE && m_eslog)
- {
- fclose(m_eslog);
- m_eslog = nullptr;
- }
- if (LOG_ES_FILE && !m_eslog)
- m_eslog = fopen("es.log", "w");
-
pci_device::device_reset();
memset(m_es_regs, 0, sizeof(m_es_regs));
memset(m_ac97_regs, 0, sizeof(m_ac97_regs));
@@ -236,7 +214,7 @@ TIMER_CALLBACK_MEMBER(es1373_device::delayed_stream_update)
void es1373_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
{
if (m_dac1.enable) {
- logerror("%s: sound_stream_update DAC1 not implemented yet\n", tag());
+ LOGMASKED(LOG_UNIMPL, "%s: sound_stream_update DAC1 not implemented yet\n", tag());
}
if (m_dac2.enable) {
@@ -248,13 +226,12 @@ void es1373_device::sound_stream_update(sound_stream &stream, std::vector<read_s
if (m_adc.enable) {
if (m_adc.format!=SCTRL_16BIT_MONO) {
- logerror("%s: sound_stream_update Only SCTRL_16BIT_MONO recorded supported\n", tag());
+ LOGMASKED(LOG_UNIMPL, "%s: sound_stream_update Only SCTRL_16BIT_MONO recorded supported\n", tag());
} else {
for (int i=0; i<outputs[0].samples(); i++) {
if (m_adc.buf_count<=m_adc.buf_size) {
- if (LOG_ES)
- logerror("%s: ADC buf_count: %i buf_size: %i buf_rptr: %i buf_wptr: %i\n", machine().describe_context(),
- m_adc.buf_count, m_adc.buf_size, m_adc.buf_rptr, m_adc.buf_wptr);
+ LOGMASKED(LOG_OTHER, "%s: ADC buf_count: %i buf_size: %i buf_rptr: %i buf_wptr: %i\n", machine().describe_context(),
+ m_adc.buf_count, m_adc.buf_size, m_adc.buf_rptr, m_adc.buf_wptr);
if ((m_adc.buf_count&0x1)) {
m_adc.buf_wptr++;
}
@@ -263,13 +240,11 @@ void es1373_device::sound_stream_update(sound_stream &stream, std::vector<read_s
if (m_adc.loop_en) {
// Keep playing
m_adc.buf_count = 0;
- if (LOG_ES)
- logerror("%s: send_audio_out ADC clearing buf_count\n", machine().describe_context());
+ LOGMASKED(LOG_OTHER, "%s: send_audio_out ADC clearing buf_count\n", machine().describe_context());
}
if (m_adc.int_en) {
m_es_regs[ES_INT_CS_STATUS] |= ICSTATUS_ADC_INT_MASK;
- if (LOG_ES)
- logerror("%s: send_audio_out Setting ADC interrupt\n", machine().describe_context());
+ LOGMASKED(LOG_OTHER, "%s: send_audio_out Setting ADC interrupt\n", machine().describe_context());
}
}
if (!(m_adc.buf_count&1) && !(m_adc.buf_wptr&0xf)) {
@@ -316,16 +291,16 @@ void es1373_device::send_audio_out(chan_info& chan, uint32_t intr_mask, write_st
if ((command & 0x4) && ((chan.buf_rptr&8)^(chan.buf_wptr&8))) {
transfer_pci_audio(chan, ES_PCI_READ);
}
- if (LOG_ES && i==0)
- logerror("%s: chan: %X samples: %i buf_count: %X buf_size: %X buf_rptr: %X buf_wptr: %X\n",
+ if (i == 0)
+ LOGMASKED(LOG_OTHER, "%s: chan: %X samples: %i buf_count: %X buf_size: %X buf_rptr: %X buf_wptr: %X\n",
machine().describe_context(), chan.number, outL.samples(), chan.buf_count, chan.buf_size, chan.buf_rptr, chan.buf_wptr);
// Buffer is 4 bytes per location, need to switch on sample mode
switch (chan.format) {
case SCTRL_8BIT_MONO:
- logerror("es1373_device::send_audio_out SCTRL_8BIT_MONO not implemented yet\n");
+ LOGMASKED(LOG_UNIMPL, "es1373_device::send_audio_out SCTRL_8BIT_MONO not implemented yet\n");
break;
case SCTRL_8BIT_STEREO:
- logerror("es1373_device::send_audio_out SCTRL_8BIT_STEREO not implemented yet\n");
+ LOGMASKED(LOG_UNIMPL, "es1373_device::send_audio_out SCTRL_8BIT_STEREO not implemented yet\n");
break;
case SCTRL_16BIT_MONO:
// The sound cache is 32 bit wide fifo, so each entry is two mono 16 bit samples
@@ -347,12 +322,6 @@ void es1373_device::send_audio_out(chan_info& chan, uint32_t intr_mask, write_st
buf_row_done = true;
break;
}
- if (LOG_ES_FILE && m_tempCount<1000000) {
- m_tempCount++;
- //logerror("es1373_device::sound_stream_update count: %i samp16: %X\n", i, samp16);
- //if (LOG_ES_FILE && m_eslog)
- //fprintf(m_eslog, "%i\n", samp16);
- }
chan.buf_count++;
if (chan.buf_count > chan.buf_size) {
if (chan.loop_en) {
@@ -360,13 +329,11 @@ void es1373_device::send_audio_out(chan_info& chan, uint32_t intr_mask, write_st
//chan.buf_count -= 1; // Should check SCTRL_P2_END_MASK
chan.buf_count = 0;
//chan.buf_rptr -= 1;
- if (LOG_ES)
- logerror("%s: send_audio_out DAC2 clearing buf_count\n", machine().describe_context());
+ LOGMASKED(LOG_OTHER, "%s: send_audio_out DAC2 clearing buf_count\n", machine().describe_context());
}
if (chan.int_en) {
m_es_regs[ES_INT_CS_STATUS] |= intr_mask;
- if (LOG_ES)
- logerror("%s: send_audio_out Setting DAC2 interrupt\n", machine().describe_context());
+ LOGMASKED(LOG_OTHER, "%s: send_audio_out Setting DAC2 interrupt\n", machine().describe_context());
}
}
if (buf_row_done && !(chan.buf_rptr&0xf)) {
@@ -382,9 +349,8 @@ void es1373_device::transfer_pci_audio(chan_info& chan, int type)
{
uint32_t pci_addr, data;
pci_addr = chan.pci_addr + (chan.pci_count<<2);
- if (LOG_ES)
- logerror("%s: transfer_pci_audio start chan: %X pci_addr: %08X pci_count: %X pci_size: %X buf_rptr: %X buf_wptr: %X\n",
- machine().describe_context(), chan.number, pci_addr, chan.pci_count, chan.pci_size, chan.buf_rptr, chan.buf_wptr);
+ LOGMASKED(LOG_OTHER, "%s: transfer_pci_audio start chan: %X pci_addr: %08X pci_count: %X pci_size: %X buf_rptr: %X buf_wptr: %X\n",
+ machine().describe_context(), chan.number, pci_addr, chan.pci_count, chan.pci_size, chan.buf_rptr, chan.buf_wptr);
// Always transfer 8 longwords
for (int i=0; i<8; i++) {
pci_addr = chan.pci_addr + (chan.pci_count<<2);
@@ -425,7 +391,7 @@ uint32_t es1373_device::calc_size(const uint8_t &format)
return 4;
break;
}
- logerror("%s: calc_size Invalid format = %X specified\n", tag(), format);
+ LOGMASKED(LOG_INVALID, "%s: calc_size Invalid format = %X specified\n", tag(), format);
return 0;
}
@@ -487,8 +453,7 @@ uint32_t es1373_device::reg_r(offs_t offset, uint32_t mem_mask)
default:
break;
}
- if (LOG_ES_REG)
- logerror("%s:ES1373 read from offset %02X = %08X & %08X\n", machine().describe_context(), offset*4, result, mem_mask);
+ LOGMASKED(LOG_REG, "%s:ES1373 read from offset %02X = %08X & %08X\n", machine().describe_context(), offset*4, result, mem_mask);
return result;
}
@@ -538,12 +503,10 @@ void es1373_device::reg_w(offs_t offset, uint32_t data, uint32_t mem_mask)
if (m_es_regs[ES_INT_CS_STATUS]&ICSTATUS_INTR_MASK && m_irq_num!=-1) {
m_irq_handler(0);
m_es_regs[ES_INT_CS_STATUS] &= ~ICSTATUS_INTR_MASK;
- if (0 && LOG_ES_REG)
- logerror("%s: es1373_device::reg_w Clearing interrupt\n", machine().describe_context());
+ LOGMASKED(LOG_IRQ, "%s: es1373_device::reg_w Clearing interrupt\n", machine().describe_context());
}
}
- if (0 && LOG_ES_REG)
- logerror("%s: es1373_device::reg_w adc_int_en: %i dac1_int_en: %i dac2_int_en: %i\n", tag(), m_adc.int_en, m_dac1.int_en, m_dac2.int_en);
+ LOGMASKED(LOG_ADC, "%s: es1373_device::reg_w adc_int_en: %i dac1_int_en: %i dac2_int_en: %i\n", tag(), m_adc.int_en, m_dac1.int_en, m_dac2.int_en);
break;
case ES_DAC2_CNT:
m_dac2.buf_count = 0;
@@ -593,8 +556,7 @@ void es1373_device::reg_w(offs_t offset, uint32_t data, uint32_t mem_mask)
case 0xc:
m_dac2.pci_count = (data>>16)&0xffff;
m_dac2.pci_size = data&0xffff;
- if (LOG_ES_REG)
- logerror("%s:ES1373 write to offset %02X = %08X & %08X\n", machine().describe_context(), offset*4, data, mem_mask);
+ LOGMASKED(LOG_REG, "%s:ES1373 write to offset %02X = %08X & %08X\n", machine().describe_context(), offset*4, data, mem_mask);
break;
default:
break;
@@ -604,7 +566,5 @@ void es1373_device::reg_w(offs_t offset, uint32_t data, uint32_t mem_mask)
break;
}
- if (LOG_ES_REG)
- logerror("%s:ES1373 write to offset %02X = %08X & %08X\n", machine().describe_context(), offset*4, data, mem_mask);
-
+ LOGMASKED(LOG_REG, "%s:ES1373 write to offset %02X = %08X & %08X\n", machine().describe_context(), offset*4, data, mem_mask);
}
diff --git a/src/devices/sound/es1373.h b/src/devices/sound/es1373.h
index 6d493391ae4..4f6bee13706 100644
--- a/src/devices/sound/es1373.h
+++ b/src/devices/sound/es1373.h
@@ -22,9 +22,7 @@ public:
void reg_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
protected:
- virtual void device_resolve_objects() override;
virtual void device_start() override;
- virtual void device_stop() override;
virtual void device_reset() override;
virtual void device_add_mconfig(machine_config &config) override;
virtual void device_post_load() override;
@@ -35,8 +33,6 @@ protected:
// Sound stream
sound_stream *m_stream;
- FILE *m_eslog;
-
private:
struct chan_info {
int number;
@@ -58,7 +54,6 @@ private:
uint32_t calc_size(const uint8_t &format);
void send_audio_out(chan_info& chan, uint32_t intr_mask, write_stream_view &outL, write_stream_view &outR);
- uint32_t m_tempCount;
emu_timer *m_timer;
address_space *m_memory_space;
devcb_write_line m_irq_handler;
diff --git a/src/devices/sound/es5503.cpp b/src/devices/sound/es5503.cpp
index 50bef4568d2..fc23656d36b 100644
--- a/src/devices/sound/es5503.cpp
+++ b/src/devices/sound/es5503.cpp
@@ -2,7 +2,7 @@
// copyright-holders:R. Belmont
/*
- ES5503 - Ensoniq ES5503 "DOC" emulator v2.1.3
+ ES5503 - Ensoniq ES5503 "DOC" emulator v2.3
By R. Belmont.
Copyright R. Belmont.
@@ -13,8 +13,8 @@
(used in the "Soundscape" series of ISA PC sound cards) followed on a fundamentally
similar architecture.
- Bugs: On the real silicon, oscillators 30 and 31 have random volume fluctuations and are
- unusable for playback. We don't attempt to emulate that. :-)
+ Bugs: On the real silicon, the uppermost enabled oscillator contributes to the output 3 times.
+ This is likely why the Apple IIgs system software doesn't let you use oscillators 30 and 31.
Additionally, in "swap" mode, there's one cycle when the switch takes place where the
oscillator's output is 0x80 (centerline) regardless of the sample data. This can
@@ -34,6 +34,8 @@
Conversely, the intro voice in FTA Delta Demo has swap on the even and one-shot on the odd and doesn't
want to loop.
2.1.3 (RB) - Fixed oscillator enable register off-by-1 which caused everything to be half a step sharp.
+ 2.2 (RB) - More precise one-shot even/swap odd behavior from hardware observations with Ian Brumby's SWAPTEST.
+ 2.3 (RB) - Sync & AM modes added, emulate the volume glitch for the highest-numbered enabled oscillator.
*/
#include "emu.h"
@@ -56,12 +58,12 @@ static constexpr int resshifts[8] = { 9, 10, 11, 12, 13, 14, 15, 16 };
// es5503_device - constructor
//-------------------------------------------------
-es5503_device::es5503_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, ES5503, tag, owner, clock),
- device_sound_interface(mconfig, *this),
- device_rom_interface(mconfig, *this),
- m_irq_func(*this),
- m_adc_func(*this)
+es5503_device::es5503_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, ES5503, tag, owner, clock),
+ device_sound_interface(mconfig, *this),
+ device_rom_interface(mconfig, *this),
+ m_irq_func(*this),
+ m_adc_func(*this, 0)
{
}
@@ -76,25 +78,42 @@ TIMER_CALLBACK_MEMBER(es5503_device::delayed_stream_update)
}
//-------------------------------------------------
-// rom_bank_updated - the rom bank has changed
+// rom_bank_pre_change - refresh the stream if the
+// ROM banking changes
//-------------------------------------------------
-void es5503_device::rom_bank_updated()
+void es5503_device::rom_bank_pre_change()
{
m_stream->update();
}
// halt_osc: handle halting an oscillator
-// chip = chip ptr
// onum = oscillator #
// type = 1 for 0 found in sample data, 0 for hit end of table size
void es5503_device::halt_osc(int onum, int type, uint32_t *accumulator, int resshift)
{
ES5503Osc *pOsc = &oscillators[onum];
ES5503Osc *pPartner = &oscillators[onum^1];
- const int mode = (pOsc->control>>1) & 3;
+ int mode = (pOsc->control>>1) & 3;
const int partnerMode = (pPartner->control>>1) & 3;
+ // check for sync mode
+ if (mode == MODE_SYNCAM)
+ {
+ if (!(onum & 1))
+ {
+ // we're even, so if the odd oscillator 1 below us is playing,
+ // restart it.
+ if (!(oscillators[onum - 1].control & 1))
+ {
+ oscillators[onum - 1].accumulator = 0;
+ }
+ }
+
+ // loop this oscillator for both sync and AM
+ mode = MODE_FREE;
+ }
+
// if 0 found in sample data or mode is not free-run, halt this oscillator
if ((mode != MODE_FREE) || (type != 0))
{
@@ -103,28 +122,28 @@ void es5503_device::halt_osc(int onum, int type, uint32_t *accumulator, int ress
else // preserve the relative phase of the oscillator when looping
{
uint16_t wtsize = pOsc->wtsize - 1;
- uint32_t altram = (*accumulator) >> resshift;
-
- if (altram > wtsize)
- {
- altram -= wtsize;
- }
- else
- {
- altram = 0;
- }
-
- *accumulator = altram << resshift;
+ *accumulator -= (wtsize << resshift);
}
- // if we're in swap mode or we're the even oscillator and the partner is in swap mode,
- // start the partner.
- if ((mode == MODE_SWAP) || ((partnerMode == MODE_SWAP) && ((onum & 1)==0)))
+ // if we're in swap mode, start the partner
+ if (mode == MODE_SWAP)
{
pPartner->control &= ~1; // clear the halt bit
pPartner->accumulator = 0; // and make sure it starts from the top (does this also need phase preservation?)
}
+ else
+ {
+ // if we're not swap and we're the even oscillator of the pair and the partner's swap
+ // but we aren't, we retrigger (!!!) Verified on IIgs hardware.
+ if ((partnerMode == MODE_SWAP) && ((onum & 1)==0))
+ {
+ pOsc->control &= ~1;
+ // preserve the phase in this case too
+ uint16_t wtsize = pOsc->wtsize - 1;
+ *accumulator -= (wtsize << resshift);
+ }
+ }
// IRQ enabled for this voice?
if (pOsc->control & 0x08)
{
@@ -153,13 +172,14 @@ void es5503_device::sound_stream_update(sound_stream &stream, std::vector<read_s
{
uint32_t wtptr = pOsc->wavetblpointer & wavemasks[pOsc->wavetblsize], altram;
uint32_t acc = pOsc->accumulator;
- uint16_t wtsize = pOsc->wtsize - 1;
+ const uint16_t wtsize = pOsc->wtsize - 1;
uint8_t ctrl = pOsc->control;
- uint16_t freq = pOsc->freq;
+ const uint16_t freq = pOsc->freq;
int16_t vol = pOsc->vol;
int8_t data = -128;
- int resshift = resshifts[pOsc->resolution] - pOsc->wavetblsize;
- uint32_t sizemask = accmasks[pOsc->wavetblsize];
+ const int resshift = resshifts[pOsc->resolution] - pOsc->wavetblsize;
+ const uint32_t sizemask = accmasks[pOsc->wavetblsize];
+ const int mode = (pOsc->control>>1) & 3;
mixp = &m_mix_buffer[0] + chan;
for (snum = 0; snum < samples; snum++)
@@ -179,7 +199,39 @@ void es5503_device::sound_stream_update(sound_stream &stream, std::vector<read_s
}
else
{
- *mixp += data * vol;
+ if (mode != MODE_SYNCAM)
+ {
+ *mixp += data * vol;
+ if (chan == (output_channels - 1))
+ {
+ *mixp += data * vol;
+ *mixp += data * vol;
+ }
+ }
+ else
+ {
+ // if we're odd, we play nothing ourselves
+ if (osc & 1)
+ {
+ if (osc < 31)
+ {
+ // if the next oscillator up is playing, it's volume becomes our control
+ if (!(oscillators[osc + 1].control & 1))
+ {
+ oscillators[osc + 1].vol = data ^ 0x80;
+ }
+ }
+ }
+ else // hard sync, both oscillators play?
+ {
+ *mixp += data * vol;
+ if (chan == (output_channels - 1))
+ {
+ *mixp += data * vol;
+ *mixp += data * vol;
+ }
+ }
+ }
mixp += output_channels;
if (altram >= wtsize)
@@ -215,9 +267,6 @@ void es5503_device::sound_stream_update(sound_stream &stream, std::vector<read_s
void es5503_device::device_start()
{
- m_irq_func.resolve_safe();
- m_adc_func.resolve_safe(0);
-
rege0 = 0xff;
save_pointer(STRUCT_MEMBER(oscillators, freq), 32);
@@ -396,7 +445,7 @@ void es5503_device::write(offs_t offset, u8 data)
break;
case 0xa0: // oscillator control
- // if a fresh key-on, reset the accumulator
+ // key on?
if ((oscillators[osc].control & 1) && (!(data&1)))
{
oscillators[osc].accumulator = 0;
diff --git a/src/devices/sound/es5503.h b/src/devices/sound/es5503.h
index 153e0a79fe1..4499d58d1f3 100644
--- a/src/devices/sound/es5503.h
+++ b/src/devices/sound/es5503.h
@@ -38,7 +38,7 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
// device_rom_interface overrides
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
TIMER_CALLBACK_MEMBER(delayed_stream_update);
diff --git a/src/devices/sound/es5506.cpp b/src/devices/sound/es5506.cpp
index 3ea90a97261..bc7412619f2 100644
--- a/src/devices/sound/es5506.cpp
+++ b/src/devices/sound/es5506.cpp
@@ -97,6 +97,8 @@ Ensoniq OTIS - ES5505 Ensoniq OTTO -
***********************************************************************************************/
+#define LOG_SERIAL (1U << 1)
+
#define VERBOSE 0
#include "logmacro.h"
@@ -171,7 +173,7 @@ es550x_device::es550x_device(const machine_config &mconfig, device_type type, co
, m_region3(*this, finder_base::DUMMY_TAG)
, m_channels(0)
, m_irq_cb(*this)
- , m_read_port_cb(*this)
+ , m_read_port_cb(*this, 0)
, m_sample_rate_changed_cb(*this)
{
}
@@ -199,9 +201,6 @@ void es550x_device::device_start()
{
// initialize the rest of the structure
m_master_clock = clock();
- m_irq_cb.resolve();
- m_read_port_cb.resolve();
- m_sample_rate_changed_cb.resolve();
m_irqv = 0x80;
// register save
@@ -299,8 +298,7 @@ void es550x_device::device_clock_changed()
m_master_clock = clock();
m_sample_rate = m_master_clock / (16 * (m_active_voices + 1));
m_stream->set_sample_rate(m_sample_rate);
- if (!m_sample_rate_changed_cb.isnull())
- m_sample_rate_changed_cb(m_sample_rate);
+ m_sample_rate_changed_cb(m_sample_rate);
}
//-------------------------------------------------
@@ -416,8 +414,7 @@ device_memory_interface::space_config_vector es5505_device::memory_space_config(
void es550x_device::update_irq_state()
{
// ES5505/6 irq line has been set high - inform the host
- if (!m_irq_cb.isnull())
- m_irq_cb(1); // IRQB set high
+ m_irq_cb(1); // IRQB set high
}
void es550x_device::update_internal_irq_state()
@@ -433,8 +430,7 @@ void es550x_device::update_internal_irq_state()
m_irqv = 0x80;
- if (!m_irq_cb.isnull())
- m_irq_cb(0); // IRQB set low
+ m_irq_cb(0); // IRQB set low
}
/**********************************************************************************************
@@ -785,7 +781,7 @@ inline void es5505_device::check_for_end_forward(es550x_voice *voice, u64 &accum
inline void es5505_device::check_for_end_reverse(es550x_voice *voice, u64 &accum)
{
- // are we past the end? */
+ // are we past the end?
if (accum < voice->start)
{
// generate interrupt if required
@@ -860,7 +856,7 @@ void es550x_device::generate_ulaw(es550x_voice *voice, s32 *dest)
check_for_end_forward(voice, accum);
}
- // two cases: second case is backward direction */
+ // two cases: second case is backward direction
else
{
// fetch two samples
@@ -1097,75 +1093,74 @@ inline void es5506_device::reg_write_low(es550x_voice *voice, offs_t offset, u32
{
switch (offset)
{
- case 0x00/8: /* CR */
+ case 0x00/8: // CR
voice->control = data & 0xffff;
LOG("voice %d, control=%04x\n", m_current_page & 0x1f, voice->control);
break;
- case 0x08/8: /* FC */
+ case 0x08/8: // FC
voice->freqcount = get_address_acc_shifted_val(data & 0x1ffff);
LOG("voice %d, freq count=%08x\n", m_current_page & 0x1f, get_address_acc_res(voice->freqcount));
break;
- case 0x10/8: /* LVOL */
+ case 0x10/8: // LVOL
voice->lvol = data & 0xffff; // low 4 bit is used for finer envelope control
LOG("voice %d, left vol=%04x\n", m_current_page & 0x1f, voice->lvol);
break;
- case 0x18/8: /* LVRAMP */
+ case 0x18/8: // LVRAMP
voice->lvramp = (data & 0xff00) >> 8;
LOG("voice %d, left vol ramp=%04x\n", m_current_page & 0x1f, voice->lvramp);
break;
- case 0x20/8: /* RVOL */
+ case 0x20/8: // RVOL
voice->rvol = data & 0xffff; // low 4 bit is used for finer envelope control
LOG("voice %d, right vol=%04x\n", m_current_page & 0x1f, voice->rvol);
break;
- case 0x28/8: /* RVRAMP */
+ case 0x28/8: // RVRAMP
voice->rvramp = (data & 0xff00) >> 8;
LOG("voice %d, right vol ramp=%04x\n", m_current_page & 0x1f, voice->rvramp);
break;
- case 0x30/8: /* ECOUNT */
+ case 0x30/8: // ECOUNT
voice->ecount = data & 0x1ff;
voice->filtcount = 0;
LOG("voice %d, envelope count=%04x\n", m_current_page & 0x1f, voice->ecount);
break;
- case 0x38/8: /* K2 */
+ case 0x38/8: // K2
voice->k2 = data & 0xffff; // low 4 bit is used for finer envelope control
LOG("voice %d, K2=%04x\n", m_current_page & 0x1f, voice->k2);
break;
- case 0x40/8: /* K2RAMP */
+ case 0x40/8: // K2RAMP
voice->k2ramp = ((data & 0xff00) >> 8) | ((data & 0x0001) << 31);
LOG("voice %d, K2 ramp=%04x\n", m_current_page & 0x1f, voice->k2ramp);
break;
- case 0x48/8: /* K1 */
+ case 0x48/8: // K1
voice->k1 = data & 0xffff; // low 4 bit is used for finer envelope control
LOG("voice %d, K1=%04x\n", m_current_page & 0x1f, voice->k1);
break;
- case 0x50/8: /* K1RAMP */
+ case 0x50/8: // K1RAMP
voice->k1ramp = ((data & 0xff00) >> 8) | ((data & 0x0001) << 31);
LOG("voice %d, K1 ramp=%04x\n", m_current_page & 0x1f, voice->k1ramp);
break;
- case 0x58/8: /* ACTV */
+ case 0x58/8: // ACTV
{
m_active_voices = data & 0x1f;
m_sample_rate = m_master_clock / (16 * (m_active_voices + 1));
m_stream->set_sample_rate(m_sample_rate);
- if (!m_sample_rate_changed_cb.isnull())
- m_sample_rate_changed_cb(m_sample_rate);
+ m_sample_rate_changed_cb(m_sample_rate);
LOG("active voices=%d, sample_rate=%d\n", m_active_voices, m_sample_rate);
break;
}
- case 0x60/8: /* MODE */
+ case 0x60/8: // MODE
// [4:3] = 00 : Single, Master, Early address mode
// [4:3] = 01 : Single, Master, Normal address mode
// [4:3] = 10 : Dual, Slave, Normal address mode
@@ -1173,11 +1168,11 @@ inline void es5506_device::reg_write_low(es550x_voice *voice, offs_t offset, u32
m_mode = data & 0x1f; // MODE1[4], MODE0[3], BCLK_EN[2], WCLK_EN[1], LRCLK_EN[0]
break;
- case 0x68/8: /* PAR - read only */
- case 0x70/8: /* IRQV - read only */
+ case 0x68/8: // PAR - read only
+ case 0x70/8: // IRQV - read only
break;
- case 0x78/8: /* PAGE */
+ case 0x78/8: // PAGE
m_current_page = data & 0x7f;
break;
}
@@ -1187,73 +1182,76 @@ inline void es5506_device::reg_write_high(es550x_voice *voice, offs_t offset, u3
{
switch (offset)
{
- case 0x00/8: /* CR */
+ case 0x00/8: // CR
voice->control = data & 0xffff;
LOG("voice %d, control=%04x\n", m_current_page & 0x1f, voice->control);
break;
- case 0x08/8: /* START */
+ case 0x08/8: // START
voice->start = get_address_acc_shifted_val(data & 0xfffff800);
LOG("voice %d, loop start=%08x\n", m_current_page & 0x1f, get_address_acc_res(voice->start));
break;
- case 0x10/8: /* END */
+ case 0x10/8: // END
voice->end = get_address_acc_shifted_val(data & 0xffffff80);
LOG("voice %d, loop end=%08x\n", m_current_page & 0x1f, get_address_acc_res(voice->end));
break;
- case 0x18/8: /* ACCUM */
+ case 0x18/8: // ACCUM
voice->accum = get_address_acc_shifted_val(data);
LOG("voice %d, accum=%08x\n", m_current_page & 0x1f, get_address_acc_res(voice->accum));
break;
- case 0x20/8: /* O4(n-1) */
- voice->o4n1 = (s32)(data << 14) >> 14;
+ case 0x20/8: // O4(n-1); TODO: 16.1 signed fixed point according to datasheet
+ voice->o4n1 = util::sext(data, 18);
LOG("voice %d, O4(n-1)=%05x\n", m_current_page & 0x1f, voice->o4n1 & 0x3ffff);
break;
- case 0x28/8: /* O3(n-1) */
- voice->o3n1 = (s32)(data << 14) >> 14;
+ case 0x28/8: // O3(n-1)
+ voice->o3n1 = util::sext(data, 18);
LOG("voice %d, O3(n-1)=%05x\n", m_current_page & 0x1f, voice->o3n1 & 0x3ffff);
break;
- case 0x30/8: /* O3(n-2) */
- voice->o3n2 = (s32)(data << 14) >> 14;
+ case 0x30/8: // O3(n-2)
+ voice->o3n2 = util::sext(data, 18);
LOG("voice %d, O3(n-2)=%05x\n", m_current_page & 0x1f, voice->o3n2 & 0x3ffff);
break;
- case 0x38/8: /* O2(n-1) */
- voice->o2n1 = (s32)(data << 14) >> 14;
+ case 0x38/8: // O2(n-1)
+ voice->o2n1 = util::sext(data, 18);
LOG("voice %d, O2(n-1)=%05x\n", m_current_page & 0x1f, voice->o2n1 & 0x3ffff);
break;
- case 0x40/8: /* O2(n-2) */
- voice->o2n2 = (s32)(data << 14) >> 14;
+ case 0x40/8: // O2(n-2)
+ voice->o2n2 = util::sext(data, 18);
LOG("voice %d, O2(n-2)=%05x\n", m_current_page & 0x1f, voice->o2n2 & 0x3ffff);
break;
- case 0x48/8: /* O1(n-1) */
- voice->o1n1 = (s32)(data << 14) >> 14;
+ case 0x48/8: // O1(n-1)
+ voice->o1n1 = util::sext(data, 18);
LOG("voice %d, O1(n-1)=%05x\n", m_current_page & 0x1f, voice->o1n1 & 0x3ffff);
break;
- case 0x50/8: /* W_ST */
+ case 0x50/8: // W_ST
m_wst = data & 0x7f;
+ LOGMASKED(LOG_SERIAL, "%s: word clock start = %02x\n", machine().describe_context(), m_wst);
break;
- case 0x58/8: /* W_END */
+ case 0x58/8: // W_END
m_wend = data & 0x7f;
+ LOGMASKED(LOG_SERIAL, "%s: word clock end = %02x\n", machine().describe_context(), m_wend);
break;
- case 0x60/8: /* LR_END */
+ case 0x60/8: // LR_END
m_lrend = data & 0x7f;
+ LOGMASKED(LOG_SERIAL, "%s: left/right clock end = %02x\n", machine().describe_context(), m_lrend);
break;
- case 0x68/8: /* PAR - read only */
- case 0x70/8: /* IRQV - read only */
+ case 0x68/8: // PAR - read only
+ case 0x70/8: // IRQV - read only
break;
- case 0x78/8: /* PAGE */
+ case 0x78/8: // PAGE
m_current_page = data & 0x7f;
break;
}
@@ -1263,63 +1261,63 @@ inline void es5506_device::reg_write_test(es550x_voice *voice, offs_t offset, u3
{
switch (offset)
{
- case 0x00/8: /* CHANNEL 0 LEFT */
+ case 0x00/8: // CHANNEL 0 LEFT
LOG("Channel 0 left test write %08x\n", data);
break;
- case 0x08/8: /* CHANNEL 0 RIGHT */
+ case 0x08/8: // CHANNEL 0 RIGHT
LOG("Channel 0 right test write %08x\n", data);
break;
- case 0x10/8: /* CHANNEL 1 LEFT */
+ case 0x10/8: // CHANNEL 1 LEFT
LOG("Channel 1 left test write %08x\n", data);
break;
- case 0x18/8: /* CHANNEL 1 RIGHT */
+ case 0x18/8: // CHANNEL 1 RIGHT
LOG("Channel 1 right test write %08x\n", data);
break;
- case 0x20/8: /* CHANNEL 2 LEFT */
+ case 0x20/8: // CHANNEL 2 LEFT
LOG("Channel 2 left test write %08x\n", data);
break;
- case 0x28/8: /* CHANNEL 2 RIGHT */
+ case 0x28/8: // CHANNEL 2 RIGHT
LOG("Channel 2 right test write %08x\n", data);
break;
- case 0x30/8: /* CHANNEL 3 LEFT */
+ case 0x30/8: // CHANNEL 3 LEFT
LOG("Channel 3 left test write %08x\n", data);
break;
- case 0x38/8: /* CHANNEL 3 RIGHT */
+ case 0x38/8: // CHANNEL 3 RIGHT
LOG("Channel 3 right test write %08x\n", data);
break;
- case 0x40/8: /* CHANNEL 4 LEFT */
+ case 0x40/8: // CHANNEL 4 LEFT
LOG("Channel 4 left test write %08x\n", data);
break;
- case 0x48/8: /* CHANNEL 4 RIGHT */
+ case 0x48/8: // CHANNEL 4 RIGHT
LOG("Channel 4 right test write %08x\n", data);
break;
- case 0x50/8: /* CHANNEL 5 LEFT */
+ case 0x50/8: // CHANNEL 5 LEFT
LOG("Channel 5 left test write %08x\n", data);
break;
- case 0x58/8: /* CHANNEL 6 RIGHT */
+ case 0x58/8: // CHANNEL 6 RIGHT
LOG("Channel 5 right test write %08x\n", data);
break;
- case 0x60/8: /* EMPTY */
+ case 0x60/8: // EMPTY
LOG("Test write EMPTY %08x\n", data);
break;
- case 0x68/8: /* PAR - read only */
- case 0x70/8: /* IRQV - read only */
+ case 0x68/8: // PAR - read only
+ case 0x70/8: // IRQV - read only
break;
- case 0x78/8: /* PAGE */
+ case 0x78/8: // PAGE
m_current_page = data & 0x7f;
break;
}
@@ -1366,70 +1364,70 @@ inline u32 es5506_device::reg_read_low(es550x_voice *voice, offs_t offset)
switch (offset)
{
- case 0x00/8: /* CR */
+ case 0x00/8: // CR
result = voice->control;
break;
- case 0x08/8: /* FC */
+ case 0x08/8: // FC
result = get_address_acc_res(voice->freqcount);
break;
- case 0x10/8: /* LVOL */
+ case 0x10/8: // LVOL
result = voice->lvol;
break;
- case 0x18/8: /* LVRAMP */
+ case 0x18/8: // LVRAMP
result = voice->lvramp << 8;
break;
- case 0x20/8: /* RVOL */
+ case 0x20/8: // RVOL
result = voice->rvol;
break;
- case 0x28/8: /* RVRAMP */
+ case 0x28/8: // RVRAMP
result = voice->rvramp << 8;
break;
- case 0x30/8: /* ECOUNT */
+ case 0x30/8: // ECOUNT
result = voice->ecount;
break;
- case 0x38/8: /* K2 */
+ case 0x38/8: // K2
result = voice->k2;
break;
- case 0x40/8: /* K2RAMP */
+ case 0x40/8: // K2RAMP
result = (voice->k2ramp << 8) | (voice->k2ramp >> 31);
break;
- case 0x48/8: /* K1 */
+ case 0x48/8: // K1
result = voice->k1;
break;
- case 0x50/8: /* K1RAMP */
+ case 0x50/8: // K1RAMP
result = (voice->k1ramp << 8) | (voice->k1ramp >> 31);
break;
- case 0x58/8: /* ACTV */
+ case 0x58/8: // ACTV
result = m_active_voices;
break;
- case 0x60/8: /* MODE */
+ case 0x60/8: // MODE
result = m_mode;
break;
- case 0x68/8: /* PAR */
- if (!m_read_port_cb.isnull())
+ case 0x68/8: // PAR
+ if (!m_read_port_cb.isunset())
result = m_read_port_cb(0) & 0x3ff; // 10 bit, 9:0
break;
- case 0x70/8: /* IRQV */
+ case 0x70/8: // IRQV
result = m_irqv;
if (!machine().side_effects_disabled())
update_internal_irq_state();
break;
- case 0x78/8: /* PAGE */
+ case 0x78/8: // PAGE
result = m_current_page;
break;
}
@@ -1443,70 +1441,70 @@ inline u32 es5506_device::reg_read_high(es550x_voice *voice, offs_t offset)
switch (offset)
{
- case 0x00/8: /* CR */
+ case 0x00/8: // CR
result = voice->control;
break;
- case 0x08/8: /* START */
+ case 0x08/8: // START
result = get_address_acc_res(voice->start);
break;
- case 0x10/8: /* END */
+ case 0x10/8: // END
result = get_address_acc_res(voice->end);
break;
- case 0x18/8: /* ACCUM */
+ case 0x18/8: // ACCUM
result = get_address_acc_res(voice->accum);
break;
- case 0x20/8: /* O4(n-1) */
+ case 0x20/8: // O4(n-1); TODO: 16.1 signed fixed point according to datasheet
result = voice->o4n1 & 0x3ffff;
break;
- case 0x28/8: /* O3(n-1) */
+ case 0x28/8: // O3(n-1)
result = voice->o3n1 & 0x3ffff;
break;
- case 0x30/8: /* O3(n-2) */
+ case 0x30/8: // O3(n-2)
result = voice->o3n2 & 0x3ffff;
break;
- case 0x38/8: /* O2(n-1) */
+ case 0x38/8: // O2(n-1)
result = voice->o2n1 & 0x3ffff;
break;
- case 0x40/8: /* O2(n-2) */
+ case 0x40/8: // O2(n-2)
result = voice->o2n2 & 0x3ffff;
break;
- case 0x48/8: /* O1(n-1) */
+ case 0x48/8: // O1(n-1)
result = voice->o1n1 & 0x3ffff;
break;
- case 0x50/8: /* W_ST */
+ case 0x50/8: // W_ST
result = m_wst;
break;
- case 0x58/8: /* W_END */
+ case 0x58/8: // W_END
result = m_wend;
break;
- case 0x60/8: /* LR_END */
+ case 0x60/8: // LR_END
result = m_lrend;
break;
- case 0x68/8: /* PAR */
- if (!m_read_port_cb.isnull())
+ case 0x68/8: // PAR
+ if (!m_read_port_cb.isunset())
result = m_read_port_cb(0) & 0x3ff; // 10 bit, 9:0
break;
- case 0x70/8: /* IRQV */
+ case 0x70/8: // IRQV
result = m_irqv;
if (!machine().side_effects_disabled())
update_internal_irq_state();
break;
- case 0x78/8: /* PAGE */
+ case 0x78/8: // PAGE
result = m_current_page;
break;
}
@@ -1518,16 +1516,16 @@ inline u32 es5506_device::reg_read_test(es550x_voice *voice, offs_t offset)
switch (offset)
{
- case 0x68/8: /* PAR */
- if (!m_read_port_cb.isnull())
+ case 0x68/8: // PAR
+ if (!m_read_port_cb.isunset())
result = m_read_port_cb(0) & 0x3ff; // 10 bit, 9:0
break;
- case 0x70/8: /* IRQV */
+ case 0x70/8: // IRQV
result = m_irqv;
break;
- case 0x78/8: /* PAGE */
+ case 0x78/8: // PAGE
result = m_current_page;
break;
}
@@ -1573,7 +1571,7 @@ inline void es5505_device::reg_write_low(es550x_voice *voice, offs_t offset, u16
{
switch (offset)
{
- case 0x00: /* CR */
+ case 0x00: // CR
voice->control |= 0xf000; // bit 15-12 always 1
if (ACCESSING_BITS_0_7)
{
@@ -1590,7 +1588,7 @@ inline void es5505_device::reg_write_low(es550x_voice *voice, offs_t offset, u16
LOG("%s:voice %d, control=%04x (raw=%04x & %04x)\n", machine().describe_context(), m_current_page & 0x1f, voice->control, data, mem_mask ^ 0xffff);
break;
- case 0x01: /* FC */
+ case 0x01: // FC
if (ACCESSING_BITS_0_7)
voice->freqcount = (voice->freqcount & ~get_address_acc_shifted_val(0x00fe, 1)) | (get_address_acc_shifted_val((data & 0x00fe), 1));
if (ACCESSING_BITS_8_15)
@@ -1598,7 +1596,7 @@ inline void es5505_device::reg_write_low(es550x_voice *voice, offs_t offset, u16
LOG("%s:voice %d, freq count=%08x\n", machine().describe_context(), m_current_page & 0x1f, get_address_acc_res(voice->freqcount, 1));
break;
- case 0x02: /* STRT (hi) */
+ case 0x02: // STRT (hi)
if (ACCESSING_BITS_0_7)
voice->start = (voice->start & ~get_address_acc_shifted_val(0x00ff0000)) | (get_address_acc_shifted_val((data & 0x00ff) << 16));
if (ACCESSING_BITS_8_15)
@@ -1606,7 +1604,7 @@ inline void es5505_device::reg_write_low(es550x_voice *voice, offs_t offset, u16
LOG("%s:voice %d, loop start=%08x\n", machine().describe_context(), m_current_page & 0x1f, get_address_acc_res(voice->start));
break;
- case 0x03: /* STRT (lo) */
+ case 0x03: // STRT (lo)
if (ACCESSING_BITS_0_7)
voice->start = (voice->start & ~get_address_acc_shifted_val(0x000000e0)) | (get_address_acc_shifted_val(data & 0x00e0));
if (ACCESSING_BITS_8_15)
@@ -1614,7 +1612,7 @@ inline void es5505_device::reg_write_low(es550x_voice *voice, offs_t offset, u16
LOG("%s:voice %d, loop start=%08x\n", machine().describe_context(), m_current_page & 0x1f, get_address_acc_res(voice->start));
break;
- case 0x04: /* END (hi) */
+ case 0x04: // END (hi)
if (ACCESSING_BITS_0_7)
voice->end = (voice->end & ~get_address_acc_shifted_val(0x00ff0000)) | (get_address_acc_shifted_val((data & 0x00ff) << 16));
if (ACCESSING_BITS_8_15)
@@ -1625,7 +1623,7 @@ inline void es5505_device::reg_write_low(es550x_voice *voice, offs_t offset, u16
LOG("%s:voice %d, loop end=%08x\n", machine().describe_context(), m_current_page & 0x1f, get_address_acc_res(voice->end));
break;
- case 0x05: /* END (lo) */
+ case 0x05: // END (lo)
if (ACCESSING_BITS_0_7)
voice->end = (voice->end & ~get_address_acc_shifted_val(0x000000e0)) | (get_address_acc_shifted_val(data & 0x00e0));
if (ACCESSING_BITS_8_15)
@@ -1636,7 +1634,7 @@ inline void es5505_device::reg_write_low(es550x_voice *voice, offs_t offset, u16
LOG("%s:voice %d, loop end=%08x\n", machine().describe_context(), m_current_page & 0x1f, get_address_acc_res(voice->end));
break;
- case 0x06: /* K2 */
+ case 0x06: // K2
if (ACCESSING_BITS_0_7)
voice->k2 = (voice->k2 & ~0x00f0) | (data & 0x00f0);
if (ACCESSING_BITS_8_15)
@@ -1644,7 +1642,7 @@ inline void es5505_device::reg_write_low(es550x_voice *voice, offs_t offset, u16
LOG("%s:voice %d, K2=%03x\n", machine().describe_context(), m_current_page & 0x1f, voice->k2 >> FILTER_SHIFT);
break;
- case 0x07: /* K1 */
+ case 0x07: // K1
if (ACCESSING_BITS_0_7)
voice->k1 = (voice->k1 & ~0x00f0) | (data & 0x00f0);
if (ACCESSING_BITS_8_15)
@@ -1652,19 +1650,19 @@ inline void es5505_device::reg_write_low(es550x_voice *voice, offs_t offset, u16
LOG("%s:voice %d, K1=%03x\n", machine().describe_context(), m_current_page & 0x1f, voice->k1 >> FILTER_SHIFT);
break;
- case 0x08: /* LVOL */
+ case 0x08: // LVOL
if (ACCESSING_BITS_8_15)
voice->lvol = (voice->lvol & ~0xff) | ((data & 0xff00) >> 8);
LOG("%s:voice %d, left vol=%02x\n", machine().describe_context(), m_current_page & 0x1f, voice->lvol);
break;
- case 0x09: /* RVOL */
+ case 0x09: // RVOL
if (ACCESSING_BITS_8_15)
voice->rvol = (voice->rvol & ~0xff) | ((data & 0xff00) >> 8);
LOG("%s:voice %d, right vol=%02x\n", machine().describe_context(), m_current_page & 0x1f, voice->rvol);
break;
- case 0x0a: /* ACC (hi) */
+ case 0x0a: // ACC (hi)
if (ACCESSING_BITS_0_7)
voice->accum = (voice->accum & ~get_address_acc_shifted_val(0x00ff0000)) | (get_address_acc_shifted_val((data & 0x00ff) << 16));
if (ACCESSING_BITS_8_15)
@@ -1672,7 +1670,7 @@ inline void es5505_device::reg_write_low(es550x_voice *voice, offs_t offset, u16
LOG("%s:voice %d, accum=%08x\n", machine().describe_context(), m_current_page & 0x1f, get_address_acc_res(voice->accum));
break;
- case 0x0b: /* ACC (lo) */
+ case 0x0b: // ACC (lo)
if (ACCESSING_BITS_0_7)
voice->accum = (voice->accum & ~get_address_acc_shifted_val(0x000000ff)) | (get_address_acc_shifted_val(data & 0x00ff));
if (ACCESSING_BITS_8_15)
@@ -1680,26 +1678,25 @@ inline void es5505_device::reg_write_low(es550x_voice *voice, offs_t offset, u16
LOG("%s:voice %d, accum=%08x\n", machine().describe_context(), m_current_page & 0x1f, get_address_acc_res(voice->accum));
break;
- case 0x0c: /* unused */
+ case 0x0c: // unused
break;
- case 0x0d: /* ACT */
+ case 0x0d: // ACT
if (ACCESSING_BITS_0_7)
{
m_active_voices = data & 0x1f;
m_sample_rate = m_master_clock / (16 * (m_active_voices + 1));
m_stream->set_sample_rate(m_sample_rate);
- if (!m_sample_rate_changed_cb.isnull())
- m_sample_rate_changed_cb(m_sample_rate);
+ m_sample_rate_changed_cb(m_sample_rate);
LOG("active voices=%d, sample_rate=%d\n", m_active_voices, m_sample_rate);
}
break;
- case 0x0e: /* IRQV - read only */
+ case 0x0e: // IRQV - read only
break;
- case 0x0f: /* PAGE */
+ case 0x0f: // PAGE
if (ACCESSING_BITS_0_7)
m_current_page = data & 0x7f;
break;
@@ -1711,7 +1708,7 @@ inline void es5505_device::reg_write_high(es550x_voice *voice, offs_t offset, u1
{
switch (offset)
{
- case 0x00: /* CR */
+ case 0x00: // CR
voice->control |= 0xf000; // bit 15-12 always 1
if (ACCESSING_BITS_0_7)
voice->control = (voice->control & ~0x00ff) | (data & 0x00ff);
@@ -1721,7 +1718,7 @@ inline void es5505_device::reg_write_high(es550x_voice *voice, offs_t offset, u1
LOG("%s:voice %d, control=%04x (raw=%04x & %04x)\n", machine().describe_context(), m_current_page & 0x1f, voice->control, data, mem_mask);
break;
- case 0x01: /* O4(n-1) */
+ case 0x01: // O4(n-1)
if (ACCESSING_BITS_0_7)
voice->o4n1 = (voice->o4n1 & ~0x00ff) | (data & 0x00ff);
if (ACCESSING_BITS_8_15)
@@ -1729,7 +1726,7 @@ inline void es5505_device::reg_write_high(es550x_voice *voice, offs_t offset, u1
LOG("%s:voice %d, O4(n-1)=%04x\n", machine().describe_context(), m_current_page & 0x1f, voice->o4n1 & 0xffff);
break;
- case 0x02: /* O3(n-1) */
+ case 0x02: // O3(n-1)
if (ACCESSING_BITS_0_7)
voice->o3n1 = (voice->o3n1 & ~0x00ff) | (data & 0x00ff);
if (ACCESSING_BITS_8_15)
@@ -1737,7 +1734,7 @@ inline void es5505_device::reg_write_high(es550x_voice *voice, offs_t offset, u1
LOG("%s:voice %d, O3(n-1)=%04x\n", machine().describe_context(), m_current_page & 0x1f, voice->o3n1 & 0xffff);
break;
- case 0x03: /* O3(n-2) */
+ case 0x03: // O3(n-2)
if (ACCESSING_BITS_0_7)
voice->o3n2 = (voice->o3n2 & ~0x00ff) | (data & 0x00ff);
if (ACCESSING_BITS_8_15)
@@ -1745,7 +1742,7 @@ inline void es5505_device::reg_write_high(es550x_voice *voice, offs_t offset, u1
LOG("%s:voice %d, O3(n-2)=%04x\n", machine().describe_context(), m_current_page & 0x1f, voice->o3n2 & 0xffff);
break;
- case 0x04: /* O2(n-1) */
+ case 0x04: // O2(n-1)
if (ACCESSING_BITS_0_7)
voice->o2n1 = (voice->o2n1 & ~0x00ff) | (data & 0x00ff);
if (ACCESSING_BITS_8_15)
@@ -1753,7 +1750,7 @@ inline void es5505_device::reg_write_high(es550x_voice *voice, offs_t offset, u1
LOG("%s:voice %d, O2(n-1)=%04x\n", machine().describe_context(), m_current_page & 0x1f, voice->o2n1 & 0xffff);
break;
- case 0x05: /* O2(n-2) */
+ case 0x05: // O2(n-2)
if (ACCESSING_BITS_0_7)
voice->o2n2 = (voice->o2n2 & ~0x00ff) | (data & 0x00ff);
if (ACCESSING_BITS_8_15)
@@ -1761,7 +1758,7 @@ inline void es5505_device::reg_write_high(es550x_voice *voice, offs_t offset, u1
LOG("%s:voice %d, O2(n-2)=%04x\n", machine().describe_context(), m_current_page & 0x1f, voice->o2n2 & 0xffff);
break;
- case 0x06: /* O1(n-1) */
+ case 0x06: // O1(n-1)
if (ACCESSING_BITS_0_7)
voice->o1n1 = (voice->o1n1 & ~0x00ff) | (data & 0x00ff);
if (ACCESSING_BITS_8_15)
@@ -1774,26 +1771,25 @@ inline void es5505_device::reg_write_high(es550x_voice *voice, offs_t offset, u1
case 0x09:
case 0x0a:
case 0x0b:
- case 0x0c: /* unused */
+ case 0x0c: // unused
break;
- case 0x0d: /* ACT */
+ case 0x0d: // ACT
if (ACCESSING_BITS_0_7)
{
m_active_voices = data & 0x1f;
m_sample_rate = m_master_clock / (16 * (m_active_voices + 1));
m_stream->set_sample_rate(m_sample_rate);
- if (!m_sample_rate_changed_cb.isnull())
- m_sample_rate_changed_cb(m_sample_rate);
+ m_sample_rate_changed_cb(m_sample_rate);
LOG("active voices=%d, sample_rate=%d\n", m_active_voices, m_sample_rate);
}
break;
- case 0x0e: /* IRQV - read only */
+ case 0x0e: // IRQV - read only
break;
- case 0x0f: /* PAGE */
+ case 0x0f: // PAGE
if (ACCESSING_BITS_0_7)
m_current_page = data & 0x7f;
break;
@@ -1805,44 +1801,44 @@ inline void es5505_device::reg_write_test(es550x_voice *voice, offs_t offset, u1
{
switch (offset)
{
- case 0x00: /* CH0L */
- case 0x01: /* CH0R */
- case 0x02: /* CH1L */
- case 0x03: /* CH1R */
- case 0x04: /* CH2L */
- case 0x05: /* CH2R */
- case 0x06: /* CH3L */
- case 0x07: /* CH3R */
+ case 0x00: // CH0L
+ case 0x01: // CH0R
+ case 0x02: // CH1L
+ case 0x03: // CH1R
+ case 0x04: // CH2L
+ case 0x05: // CH2R
+ case 0x06: // CH3L
+ case 0x07: // CH3R
break;
- case 0x08: /* SERMODE */
+ case 0x08: // SERMODE
m_mode |= 0x7f8; // bit 10-3 always 1
if (ACCESSING_BITS_8_15)
m_mode = (m_mode & ~0xf800) | (data & 0xf800); // MSB[4:0] (unknown purpose)
if (ACCESSING_BITS_0_7)
m_mode = (m_mode & ~0x0007) | (data & 0x0007); // SONY/BB, TEST, A/D
+ LOGMASKED(LOG_SERIAL, "%s: serial mode = %04x & %04x", machine().describe_context(), m_mode, mem_mask);
break;
- case 0x09: /* PAR */
+ case 0x09: // PAR
break;
- case 0x0d: /* ACT */
+ case 0x0d: // ACT
if (ACCESSING_BITS_0_7)
{
m_active_voices = data & 0x1f;
m_sample_rate = m_master_clock / (16 * (m_active_voices + 1));
m_stream->set_sample_rate(m_sample_rate);
- if (!m_sample_rate_changed_cb.isnull())
- m_sample_rate_changed_cb(m_sample_rate);
+ m_sample_rate_changed_cb(m_sample_rate);
LOG("active voices=%d, sample_rate=%d\n", m_active_voices, m_sample_rate);
}
break;
- case 0x0e: /* IRQV - read only */
+ case 0x0e: // IRQV - read only
break;
- case 0x0f: /* PAGE */
+ case 0x0f: // PAGE
if (ACCESSING_BITS_0_7)
m_current_page = data & 0x7f;
break;
@@ -1882,68 +1878,68 @@ inline u16 es5505_device::reg_read_low(es550x_voice *voice, offs_t offset)
switch (offset)
{
- case 0x00: /* CR */
+ case 0x00: // CR
result = voice->control | 0xf000;
break;
- case 0x01: /* FC */
+ case 0x01: // FC
result = get_address_acc_res(voice->freqcount, 1);
break;
- case 0x02: /* STRT (hi) */
+ case 0x02: // STRT (hi)
result = get_address_acc_res(voice->start) >> 16;
break;
- case 0x03: /* STRT (lo) */
+ case 0x03: // STRT (lo)
result = get_address_acc_res(voice->start);
break;
- case 0x04: /* END (hi) */
+ case 0x04: // END (hi)
result = get_address_acc_res(voice->end) >> 16;
break;
- case 0x05: /* END (lo) */
+ case 0x05: // END (lo)
result = get_address_acc_res(voice->end);
break;
- case 0x06: /* K2 */
+ case 0x06: // K2
result = voice->k2;
break;
- case 0x07: /* K1 */
+ case 0x07: // K1
result = voice->k1;
break;
- case 0x08: /* LVOL */
+ case 0x08: // LVOL
result = voice->lvol << 8;
break;
- case 0x09: /* RVOL */
+ case 0x09: // RVOL
result = voice->rvol << 8;
break;
- case 0x0a: /* ACC (hi) */
+ case 0x0a: // ACC (hi)
result = get_address_acc_res(voice->accum) >> 16;
break;
- case 0x0b: /* ACC (lo) */
+ case 0x0b: // ACC (lo)
result = get_address_acc_res(voice->accum);
break;
- case 0x0c: /* unused */
+ case 0x0c: // unused
break;
- case 0x0d: /* ACT */
+ case 0x0d: // ACT
result = m_active_voices;
break;
- case 0x0e: /* IRQV */
+ case 0x0e: // IRQV
result = m_irqv;
if (!machine().side_effects_disabled())
update_internal_irq_state();
break;
- case 0x0f: /* PAGE */
+ case 0x0f: // PAGE
result = m_current_page;
break;
}
@@ -1957,37 +1953,37 @@ inline u16 es5505_device::reg_read_high(es550x_voice *voice, offs_t offset)
switch (offset)
{
- case 0x00: /* CR */
+ case 0x00: // CR
result = voice->control | 0xf000;
break;
- case 0x01: /* O4(n-1) */
+ case 0x01: // O4(n-1)
result = voice->o4n1 & 0xffff;
break;
- case 0x02: /* O3(n-1) */
+ case 0x02: // O3(n-1)
result = voice->o3n1 & 0xffff;
break;
- case 0x03: /* O3(n-2) */
+ case 0x03: // O3(n-2)
result = voice->o3n2 & 0xffff;
break;
- case 0x04: /* O2(n-1) */
+ case 0x04: // O2(n-1)
result = voice->o2n1 & 0xffff;
break;
- case 0x05: /* O2(n-2) */
+ case 0x05: // O2(n-2)
result = voice->o2n2 & 0xffff;
break;
- case 0x06: /* O1(n-1) */
- /* special case for the Taito F3 games: they set the accumulator on a stopped */
- /* voice and assume the filters continue to process the data. They then read */
- /* the O1(n-1) in order to extract raw data from the sound ROMs. Since we don't */
- /* want to waste time filtering stopped channels, we just look for a read from */
- /* this register on a stopped voice, and return the raw sample data at the */
- /* accumulator */
+ case 0x06: // O1(n-1)
+ // special case for the Taito F3 games: they set the accumulator on a stopped
+ // voice and assume the filters continue to process the data. They then read
+ // the O1(n-1) in order to extract raw data from the sound ROMs. Since we don't
+ // want to waste time filtering stopped channels, we just look for a read from
+ // this register on a stopped voice, and return the raw sample data at the
+ // accumulator
if ((voice->control & CONTROL_STOPMASK))
{
voice->o1n1 = read_sample(voice, get_integer_addr(voice->accum));
@@ -2001,20 +1997,20 @@ inline u16 es5505_device::reg_read_high(es550x_voice *voice, offs_t offset)
case 0x09:
case 0x0a:
case 0x0b:
- case 0x0c: /* unused */
+ case 0x0c: // unused
break;
- case 0x0d: /* ACT */
+ case 0x0d: // ACT
result = m_active_voices;
break;
- case 0x0e: /* IRQV */
+ case 0x0e: // IRQV
result = m_irqv;
if (!machine().side_effects_disabled())
update_internal_irq_state();
break;
- case 0x0f: /* PAGE */
+ case 0x0f: // PAGE
result = m_current_page;
break;
}
@@ -2028,37 +2024,37 @@ inline u16 es5505_device::reg_read_test(es550x_voice *voice, offs_t offset)
switch (offset)
{
- case 0x00: /* CH0L */
- case 0x01: /* CH0R */
- case 0x02: /* CH1L */
- case 0x03: /* CH1R */
- case 0x04: /* CH2L */
- case 0x05: /* CH2R */
- case 0x06: /* CH3L */
- case 0x07: /* CH3R */
+ case 0x00: // CH0L
+ case 0x01: // CH0R
+ case 0x02: // CH1L
+ case 0x03: // CH1R
+ case 0x04: // CH2L
+ case 0x05: // CH2R
+ case 0x06: // CH3L
+ case 0x07: // CH3R
break;
- case 0x08: /* SERMODE */
+ case 0x08: // SERMODE
result = m_mode | 0x7f8;
break;
- case 0x09: /* PAR */
- if (!m_read_port_cb.isnull())
+ case 0x09: // PAR
+ if (!m_read_port_cb.isunset())
result = m_read_port_cb(0) & 0xffc0; // 10 bit, 15:6
break;
- /* The following are global, and thus accessible form all pages */
- case 0x0d: /* ACT */
+ // The following are global, and thus accessible form all pages
+ case 0x0d: // ACT
result = m_active_voices;
break;
- case 0x0e: /* IRQV */
+ case 0x0e: // IRQV
result = m_irqv;
if (!machine().side_effects_disabled())
update_internal_irq_state();
break;
- case 0x0f: /* PAGE */
+ case 0x0f: // PAGE
result = m_current_page;
break;
}
diff --git a/src/devices/sound/es8712.cpp b/src/devices/sound/es8712.cpp
index 7efb7647be4..ae21168ef77 100644
--- a/src/devices/sound/es8712.cpp
+++ b/src/devices/sound/es8712.cpp
@@ -70,9 +70,6 @@ void es8712_device::device_add_mconfig(machine_config &config)
void es8712_device::device_start()
{
- m_reset_handler.resolve_safe();
- m_msm_write_cb.resolve_safe();
-
es8712_state_save_register();
}
@@ -95,16 +92,7 @@ void es8712_device::device_reset()
//-------------------------------------------------
-// rom_bank_updated - nothing for now
-//-------------------------------------------------
-
-void es8712_device::rom_bank_updated()
-{
-}
-
-
-//-------------------------------------------------
-// state save support for MAME
+// state save support for MAME
//-------------------------------------------------
void es8712_device::es8712_state_save_register()
@@ -127,6 +115,7 @@ void es8712_device::es8712_state_save_register()
void es8712_device::play()
{
assert(m_msm.found());
+
if (m_start < m_end)
{
if (!m_playing)
@@ -181,23 +170,31 @@ void es8712_device::write(offs_t offset, uint8_t data)
{
switch (offset)
{
- case 00: m_start &= 0x000fff00;
- m_start |= ((data & 0xff) << 0); break;
- case 01: m_start &= 0x000f00ff;
- m_start |= ((data & 0xff) << 8); break;
- case 02: m_start &= 0x0000ffff;
- m_start |= ((data & 0x0f) << 16); break;
- case 03: m_end &= 0x000fff00;
- m_end |= ((data & 0xff) << 0); break;
- case 04: m_end &= 0x000f00ff;
- m_end |= ((data & 0xff) << 8); break;
- case 05: m_end &= 0x0000ffff;
- m_end |= ((data & 0x0f) << 16); break;
- case 06:
- play(); break;
- default: break;
+ case 0: case 1: case 2:
+ {
+ uint8_t shift = offset * 8;
+ m_start &= ~(0xff << shift);
+ m_start |= data << shift;
+ m_start &= 0xfffff;
+ break;
+ }
+
+ case 3: case 4: case 5:
+ {
+ uint8_t shift = (offset - 3) * 8;
+ m_end &= ~(0xff << shift);
+ m_end |= data << shift;
+ m_end &= 0xfffff;
+ break;
+ }
+
+ case 6:
+ play();
+ break;
+
+ default:
+ break;
}
- m_start &= 0xfffff; m_end &= 0xfffff;
}
uint8_t es8712_device::read(offs_t offset)
@@ -218,6 +215,7 @@ void es8712_device::msm_int(int state)
{
if (!state || !m_playing)
return;
+
if (m_base_offset >= 0x100000 || m_base_offset > m_end)
{
m_playing = 0;
diff --git a/src/devices/sound/es8712.h b/src/devices/sound/es8712.h
index 02a51a23c16..27fc5bab853 100644
--- a/src/devices/sound/es8712.h
+++ b/src/devices/sound/es8712.h
@@ -41,8 +41,6 @@ protected:
virtual void device_start() override;
virtual void device_reset() override;
- virtual void rom_bank_updated() override;
-
private:
void es8712_state_save_register();
diff --git a/src/devices/sound/esqpump.cpp b/src/devices/sound/esqpump.cpp
index 38b95a15bed..af1a59db799 100644
--- a/src/devices/sound/esqpump.cpp
+++ b/src/devices/sound/esqpump.cpp
@@ -9,7 +9,7 @@
***************************************************************************/
#include "emu.h"
-#include "sound/esqpump.h"
+#include "esqpump.h"
DEFINE_DEVICE_TYPE(ESQ_5505_5510_PUMP, esq_5505_5510_pump_device, "esq_5505_5510_pump", "Ensoniq 5505/5506 to 5510 interface")
diff --git a/src/devices/sound/flt_biquad.cpp b/src/devices/sound/flt_biquad.cpp
index a023c1ff0a5..6ae1bde0109 100644
--- a/src/devices/sound/flt_biquad.cpp
+++ b/src/devices/sound/flt_biquad.cpp
@@ -14,10 +14,11 @@
* It uses the Q factor directly in the filter definitions, rather than the damping factor (1/Q)
* It implements every common type of digital biquad filter which I could find documentation for.
* The filter is Direct-form II instead of Direct-form I, which results in shorter compiled code.
+ * Optional direct control of the 5 normalized biquad parameters for a custom/raw parameter filter.
Possibly useful features which aren't implemented because nothing uses them yet:
* More Sallen-Key filter variations (band-pass, high-pass)
- * Direct control of the 5 normalized biquad parameters for a custom/raw parameter filter.
+
*/
#include "emu.h"
#include "flt_biquad.h"
@@ -84,6 +85,17 @@ filter_biquad_device& filter_biquad_device::setup(filter_biquad_device::biquad_p
m_gain = p.gain;
return *this;
}
+filter_biquad_device& filter_biquad_device::setup_raw(double a1, double a2, double b0, double b1, double b2)
+{
+ m_type = biquad_type::RAWPARAMS;
+ m_a1 = a1;
+ m_a2 = a2;
+ m_b0 = b0;
+ m_b1 = b1;
+ m_b2 = b2;
+ return *this;
+}
+
// modify an existing instance with new filter parameters
void filter_biquad_device::modify(biquad_type type, double fc, double q, double gain)
@@ -104,14 +116,30 @@ void filter_biquad_device::modify(filter_biquad_device::biquad_params p)
m_gain = p.gain;
recalc();
}
+void filter_biquad_device::modify_raw(double a1, double a2, double b0, double b1, double b2)
+{
+ m_stream->update();
+ m_type = biquad_type::RAWPARAMS;
+ m_a1 = a1;
+ m_a2 = a2;
+ m_b0 = b0;
+ m_b1 = b1;
+ m_b2 = b2;
+ recalc();
+}
+
//-------------------------------------------------
// Filter setup helpers for various filter models
//-------------------------------------------------
+// NOTE: if a resistor doesn't exist, pass a value of RES_M(999.99) or the like, i.e. an 'infinite resistor'
+// NOTE: if a resistor is a direct short, set its resistance to RES_R(0.001)
+
// Sallen-Key filters
/* Setup a biquad filter structure based on a single op-amp Sallen-Key low-pass filter circuit.
+ * This is sometimes, incorrectly, called a "Butterworth" filter structure.
*
* .----------------------------.
* | |
@@ -154,9 +182,6 @@ filter_biquad_device::biquad_params filter_biquad_device::opamp_sk_lowpass_calc(
{
fatalerror("filter_biquad_device::opamp_sk_lowpass_calc() - no parameters can be 0; parameters were: r1: %f, r2: %f, r3: %f, r4: %f, c1: %f, c2: %f", r1, r2, r3, r4, c1, c2); /* Filter can not be setup. Undefined results. */
}
- // NOTE: if R3 doesn't exist (no link to ground), pass a value of RES_M(999.99) or the like, i.e. an 'infinite resistor'
- // NOTE: if R4 is a direct short, set its resistance to RES_R(0.001)
- // NOTE: if R3 doesn't exist AND R4 is a direct short, follow both rules above.
r.type = biquad_type::LOWPASS;
r.gain = 1.0 + (r4 / r3); // == (r3 + r4) / r3
r.fc = 1.0 / (2 * M_PI * sqrt(r1 * r2 * c1 * c2));
@@ -179,6 +204,7 @@ filter_biquad_device::biquad_params filter_biquad_device::opamp_sk_lowpass_calc(
* a dead short, and c1 omitted. set both c1 and r2 to 0 in this case.
* NOTE3: a variant of NOTE2 has only the c1 capacitor left off, and r2 present. if so,
* set c1 to 0 and r2 to its expected value.
+ * TODO: make this compatible with the RES_M(999.99) and RES_R(0.001) rules!
*
* .--------+---------.
* | | |
@@ -215,7 +241,7 @@ filter_biquad_device::biquad_params filter_biquad_device::opamp_mfb_lowpass_calc
}
r.gain = -r3 / r1;
r.q = (M_SQRT2 / 2.0);
- if (c1 == 0) // if both R2 and C1 are 0, it is the 'proper' first order case. There do exist some unusual filters where R2 is not 0, though. In both cases this yields a single-pole filter with limited configurable gain, and a Q of ~0.707. R2 being zero makes the (r1 * r3) numerator term cancel out to 1.0.
+ if (c1 == 0) // if both R2 and C1 are 0, it is the 'proper' first order case. If C1 is 0 (Williams...) the filter is 1st order. There do exist some unusual filters where R2 is not 0, though. In both cases this yields a single-pole filter with limited configurable gain, and a Q of ~0.707. R2 being zero makes the (r1 * r3) numerator term cancel out to 1.0.
{
r.fc = (r1 * r3) / (2 * M_PI * ((r1 * r2) + (r1 * r3) + (r2 * r3)) * r3 * c2);
r.type = biquad_type::LOWPASS1P;
@@ -234,14 +260,14 @@ filter_biquad_device::biquad_params filter_biquad_device::opamp_mfb_lowpass_calc
/* Setup a biquad filter structure based on a single op-amp Multiple-Feedback band-pass filter circuit.
* This is sometimes called a "modified Deliyannis" or "Deliyannis-friend" filter circuit,
- * or an "Infinite Gain Multiple-Feedback [band-pass] Filter", or "IGMF".
+ * or an "Infinite Gain Multiple-Feedback [band-pass] Filter" aka "IGMF".
* NOTE: vRef is not definable when setting up the filter, and is assumed to be grounded.
* If the analog effects caused by vRef are important to the operation of the specific filter
* in question, a netlist implementation may work better under those circumstances.
* TODO: There is a documented modification to this filter which adds a resistor ladder between
* ground and the op-amp output, with the 'rung' of the ladder connecting to the + input of
* the op-amp, and this allows more control of the filter.
- * NOTE2: If r2 is not used, then set it to 0 ohms, the code will switch to an Infinite Gain MFB Bandpass
+ * NOTE2: If r2 is not used, then set it to RES_M(999.99), the code will effectively be an Infinite Gain MFB Bandpass.
*
* .--------+---------.
* | | |
@@ -259,27 +285,18 @@ filter_biquad_device::biquad_params filter_biquad_device::opamp_mfb_lowpass_calc
*/
filter_biquad_device& filter_biquad_device::opamp_mfb_bandpass_setup(double r1, double r2, double r3, double c1, double c2)
{
- if ((r1 == 0) || (r3 == 0) || (c1 == 0) || (c2 == 0))
+ if ((r1 == 0) || (r2 == 0) || (r3 == 0) || (c1 == 0) || (c2 == 0))
{
- fatalerror("filter_biquad_device::opamp_mfb_bandpass_setup() - only r2 can be 0; parameters were: r1: %f, r2: %f, r3: %f, c1: %f, c2: %f", r1, r2, r3, c1, c2); /* Filter can not be setup. Undefined results. */
+ fatalerror("filter_biquad_device::opamp_mfb_bandpass_setup() - no parameters can be 0; parameters were: r1: %f, r2: %f, r3: %f, c1: %f, c2: %f", r1, r2, r3, c1, c2); /* Filter can not be setup. Undefined results. */
}
- double r_in, gain;
+ double const r_in = 1.0 / (1.0/r1 + 1.0/r2); // TODO: verify
+ // gain = (r2 / (r1 + r2)) * (-r3 / r_in * c2 / (c1 + c2)); // ??? wrong?
+ double const gain = -r3 / (2.0 * r1);
+ // q = sqrt(r3 / r_in * c1 * c2) / (c1 + c2); // ??? wrong?
+ double const q = 0.5 * sqrt(r3 / r1);
- if (r2 == 0)
- {
- gain = 1;
- r_in = r1;
- }
- else
- {
- gain = r2 / (r1 + r2);
- r_in = 1.0 / (1.0/r1 + 1.0/r2);
- }
-
- double const fc = 1.0 / (2 * M_PI * sqrt(r_in * r3 * c1 * c2)); // technically this is the center frequency of the bandpass
- double const q = sqrt(r3 / r_in * c1 * c2) / (c1 + c2);
- gain *= -r3 / r_in * c2 / (c1 + c2);
+ double const fc = 1.0 / (sqrt(r_in * r3 * c1 * c2)); // technically this is the center frequency of the bandpass
#ifdef FLT_BIQUAD_DEBUG_SETUP
logerror("filter_biquad_device::opamp_mfb_bandpass_setup() yields: fc = %f, Q = %f, gain = %f\n", fc, q, gain);
#endif
@@ -434,6 +451,9 @@ void filter_biquad_device::sound_stream_update(sound_stream &stream, std::vector
*/
void filter_biquad_device::recalc()
{
+ if (m_type == biquad_type::RAWPARAMS)
+ return; // if we're dealing with raw parameters, just return, don't touch anything.
+
double const MGain = fabs(m_gain); // absolute multiplicative gain
double const DBGain = log10(MGain) * 20.0; // gain in dB
double const AMGain = pow(10, fabs(DBGain) / 20.0); // multiplicative gain of absolute DB
@@ -442,9 +462,6 @@ void filter_biquad_device::recalc()
double const KoverQ = K / m_q;
double normal = 1.0 / (1.0 + KoverQ + Ksquared);
- m_a1 = 2.0 * (Ksquared - 1.0) * normal;
- m_a2 = (1.0 - KoverQ + Ksquared) * normal;
-
switch (m_type)
{
case biquad_type::LOWPASS1P:
diff --git a/src/devices/sound/flt_biquad.h b/src/devices/sound/flt_biquad.h
index 9ac3f820893..e565aa6da8a 100644
--- a/src/devices/sound/flt_biquad.h
+++ b/src/devices/sound/flt_biquad.h
@@ -24,7 +24,8 @@ public:
NOTCH,
PEAK,
LOWSHELF,
- HIGHSHELF
+ HIGHSHELF,
+ RAWPARAMS
};
struct biquad_params
@@ -44,7 +45,12 @@ public:
void modify(biquad_type type, double fc, double q, double gain);
void modify(biquad_params p);
- // helper setup functions to create common filters representable by biquad filters:
+ // set up the filter with raw biquad coefficients
+ filter_biquad_device& setup_raw(double a1, double a2, double b0, double b1, double b2);
+ void modify_raw(double a1, double a2, double b0, double b1, double b2);
+
+ // Helper setup functions to create common filters representable by biquad filters:
+ // (and, as needed, modify/update/recalc helpers)
// Sallen-Key low-pass
filter_biquad_device& opamp_sk_lowpass_setup(double r1, double r2, double r3, double r4, double c1, double c2);
diff --git a/src/devices/sound/flt_vol.cpp b/src/devices/sound/flt_vol.cpp
index 6e63fa1ae1b..d5d92209cac 100644
--- a/src/devices/sound/flt_vol.cpp
+++ b/src/devices/sound/flt_vol.cpp
@@ -15,7 +15,7 @@ filter_volume_device::filter_volume_device(const machine_config &mconfig, const
device_t(mconfig, FILTER_VOLUME, tag, owner, clock),
device_sound_interface(mconfig, *this),
m_stream(nullptr),
- m_gain(0)
+ m_gain(1.0f)
{
}
@@ -26,9 +26,7 @@ filter_volume_device::filter_volume_device(const machine_config &mconfig, const
void filter_volume_device::device_start()
{
- m_gain = 1.0;
m_stream = stream_alloc(1, 1, SAMPLE_RATE_OUTPUT_ADAPTIVE);
-
save_item(NAME(m_gain));
}
@@ -44,8 +42,11 @@ void filter_volume_device::sound_stream_update(sound_stream &stream, std::vector
}
-void filter_volume_device::flt_volume_set_volume(float volume)
+filter_volume_device &filter_volume_device::set_gain(float gain)
{
- m_stream->update();
- m_gain = volume;
+ if (m_stream)
+ m_stream->update();
+ m_gain = gain;
+
+ return *this;
}
diff --git a/src/devices/sound/flt_vol.h b/src/devices/sound/flt_vol.h
index 022a980a6a5..8104833e9ac 100644
--- a/src/devices/sound/flt_vol.h
+++ b/src/devices/sound/flt_vol.h
@@ -17,7 +17,8 @@ class filter_volume_device : public device_t, public device_sound_interface
public:
filter_volume_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
- void flt_volume_set_volume(float volume);
+ filter_volume_device &set_gain(float gain); // also may be used in mcfg to set initial value (default is 1.0)
+ float gain() { return m_gain; }
protected:
// device-level overrides
diff --git a/src/devices/sound/gaelco.cpp b/src/devices/sound/gaelco.cpp
index 62553b44413..2c04678ff62 100644
--- a/src/devices/sound/gaelco.cpp
+++ b/src/devices/sound/gaelco.cpp
@@ -40,10 +40,10 @@ Registers per channel:
#include "wavwrite.h"
-#define VERBOSE_SOUND 0
-#define VERBOSE_READ_WRITES 0
-#define LOG_SOUND(x) do { if (VERBOSE_SOUND) logerror x; } while (0)
-#define LOG_READ_WRITES(x) do { if (VERBOSE_READ_WRITES) logerror x; } while (0)
+#define LOG_SOUND (1U << 1)
+#define LOG_READ_WRITES (1U << 2)
+#define VERBOSE (0)
+#include "logmacro.h"
//#define ALT_MIX
@@ -135,7 +135,7 @@ void gaelco_gae1_device::sound_stream_update(sound_stream &stream, std::vector<r
}
else
{
- LOG_SOUND(("(GAE1) Playing unknown sample format in channel: %02d, type: %02x, bank: %02x, end: %08x, Length: %04x\n", ch, type, bank, end_pos, m_sndregs[base_offset + 3]));
+ LOGMASKED(LOG_SOUND, "(GAE1) Playing unknown sample format in channel: %02d, type: %02x, bank: %02x, end: %08x, Length: %04x\n", ch, type, bank, end_pos, m_sndregs[base_offset + 3]);
//channel->active = 0;
// play2000 expects these to expire, are they valid? this is unrelated to the missing sounds in touchgo which never hits here
m_sndregs[base_offset + 3]--;
@@ -192,7 +192,7 @@ void gaelco_gae1_device::sound_stream_update(sound_stream &stream, std::vector<r
uint16_t gaelco_gae1_device::gaelcosnd_r(offs_t offset)
{
- LOG_READ_WRITES(("%s: (GAE1): read from %04x\n", machine().describe_context(), offset));
+ LOGMASKED(LOG_READ_WRITES, "%s: (GAE1): read from %04x\n", machine().describe_context(), offset);
/* first update the stream to this point in time */
m_stream->update();
@@ -208,7 +208,7 @@ void gaelco_gae1_device::gaelcosnd_w(offs_t offset, uint16_t data, uint16_t mem_
{
sound_channel *channel = &m_channel[offset >> 3];
- LOG_READ_WRITES(("%s: (GAE1): write %04x to %04x\n", machine().describe_context(), data, offset));
+ LOGMASKED(LOG_READ_WRITES, "%s: (GAE1): write %04x to %04x\n", machine().describe_context(), data, offset);
/* first update the stream to this point in time */
m_stream->update();
@@ -221,7 +221,7 @@ void gaelco_gae1_device::gaelcosnd_w(offs_t offset, uint16_t data, uint16_t mem_
// if sample end position isn't 0, and length isn't 0
if ((m_sndregs[offset - 1] != 0) && (data != 0))
{
- LOG_SOUND(("(GAE1) Playing or Queuing 1st chunk in channel: %02d, type: %02x, bank: %02x, end: %08x, Length: %04x\n", offset >> 3, (m_sndregs[offset - 2] >> 4) & 0x0f, m_sndregs[offset - 2] & 0x03, m_sndregs[offset - 1] << 8, data));
+ LOGMASKED(LOG_SOUND, "(GAE1) Playing or Queuing 1st chunk in channel: %02d, type: %02x, bank: %02x, end: %08x, Length: %04x\n", offset >> 3, (m_sndregs[offset - 2] >> 4) & 0x0f, m_sndregs[offset - 2] & 0x03, m_sndregs[offset - 1] << 8, data);
channel->loop = 1;
@@ -244,7 +244,7 @@ void gaelco_gae1_device::gaelcosnd_w(offs_t offset, uint16_t data, uint16_t mem_
// if sample end position isn't 0, and length isn't 0
if ((m_sndregs[offset - 1] != 0) && (data != 0))
{
- LOG_SOUND(("(GAE1) Playing or Queuing 2nd chunk in channel: %02d, type: %02x, bank: %02x, end: %08x, Length: %04x\n", offset >> 3, (m_sndregs[offset - 2] >> 4) & 0x0f, m_sndregs[offset - 2] & 0x03, m_sndregs[offset - 1] << 8, data));
+ LOGMASKED(LOG_SOUND, "(GAE1) Playing or Queuing 2nd chunk in channel: %02d, type: %02x, bank: %02x, end: %08x, Length: %04x\n", offset >> 3, (m_sndregs[offset - 2] >> 4) & 0x0f, m_sndregs[offset - 2] & 0x03, m_sndregs[offset - 1] << 8, data);
channel->loop = 1;
@@ -327,7 +327,7 @@ void gaelco_gae1_device::device_clock_changed()
}
-void gaelco_gae1_device::rom_bank_updated()
+void gaelco_gae1_device::rom_bank_pre_change()
{
m_stream->update();
}
diff --git a/src/devices/sound/gaelco.h b/src/devices/sound/gaelco.h
index 349a2a1071b..3bfac39f988 100644
--- a/src/devices/sound/gaelco.h
+++ b/src/devices/sound/gaelco.h
@@ -46,7 +46,7 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
// device_rom_interface overrides
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
private:
static constexpr int NUM_CHANNELS = 0x07;
diff --git a/src/devices/sound/gew.cpp b/src/devices/sound/gew.cpp
new file mode 100644
index 00000000000..c9d3ea3e500
--- /dev/null
+++ b/src/devices/sound/gew.cpp
@@ -0,0 +1,641 @@
+// license:BSD-3-Clause
+// copyright-holders:Miguel Angel Horna
+
+#include "emu.h"
+#include "gew.h"
+#include "wavwrite.h"
+
+ALLOW_SAVE_TYPE(gew_pcm_device::state_t); // allow save_item on a non-fundamental type
+
+/*******************************
+ ENVELOPE SECTION
+*******************************/
+
+// Times are based on a 44100Hz timebase. It's adjusted to the actual sampling rate on startup
+
+const double gew_pcm_device::BASE_TIMES[64] = {
+ 0, 0, 0, 0,
+ 6222.95, 4978.37, 4148.66, 3556.01,
+ 3111.47, 2489.21, 2074.33, 1778.00,
+ 1555.74, 1244.63, 1037.19, 889.02,
+ 777.87, 622.31, 518.59, 444.54,
+ 388.93, 311.16, 259.32, 222.27,
+ 194.47, 155.60, 129.66, 111.16,
+ 97.23, 77.82, 64.85, 55.60,
+ 48.62, 38.91, 32.43, 27.80,
+ 24.31, 19.46, 16.24, 13.92,
+ 12.15, 9.75, 8.12, 6.98,
+ 6.08, 4.90, 4.08, 3.49,
+ 3.04, 2.49, 2.13, 1.90,
+ 1.72, 1.41, 1.18, 1.04,
+ 0.91, 0.73, 0.59, 0.50,
+ 0.45, 0.45, 0.45, 0.45
+};
+
+constexpr uint32_t gew_pcm_device::TL_SHIFT;
+constexpr uint32_t gew_pcm_device::EG_SHIFT;
+
+void gew_pcm_device::retrigger_sample(slot_t &slot)
+{
+ slot.m_offset = 0;
+ slot.m_prev_sample = 0;
+ slot.m_total_level = slot.m_dest_total_level << TL_SHIFT;
+
+ envelope_generator_calc(slot);
+ slot.m_envelope_gen.m_state = state_t::ATTACK;
+ slot.m_envelope_gen.m_volume = 0;
+
+#if MULTIPCM_LOG_SAMPLES
+ dump_sample(slot);
+#endif
+}
+
+void gew_pcm_device::update_step(slot_t &slot)
+{
+ const uint8_t oct = (slot.m_octave - 1) & 0xf;
+ uint32_t pitch = m_freq_step_table[slot.m_pitch];
+ if (oct & 0x8)
+ {
+ pitch >>= (16 - oct);
+ }
+ else
+ {
+ pitch <<= oct;
+ }
+ slot.m_step = pitch / m_rate;
+}
+
+void gew_pcm_device::envelope_generator_init(const double (&rates)[64], double attack_decay_ratio)
+{
+ for (int32_t i = 4; i < 0x40; ++i)
+ {
+ // Times are based on 44100Hz clock, adjust to real chip clock
+ m_attack_step[i] = (float)(0x400 << EG_SHIFT) / (float)(rates[i] * 44100.0 / 1000.0);
+ m_decay_release_step[i] = (float)(0x400 << EG_SHIFT) / (float)(rates[i] * attack_decay_ratio * 44100.0 / 1000.0);
+ }
+ m_attack_step[0] = m_attack_step[1] = m_attack_step[2] = m_attack_step[3] = 0;
+ m_attack_step[0x3f] = 0x400 << EG_SHIFT;
+ m_decay_release_step[0] = m_decay_release_step[1] = m_decay_release_step[2] = m_decay_release_step[3] = 0;
+}
+
+int32_t gew_pcm_device::envelope_generator_update(slot_t &slot)
+{
+ switch (slot.m_envelope_gen.m_state)
+ {
+ case state_t::ATTACK:
+ slot.m_envelope_gen.m_volume += slot.m_envelope_gen.m_attack_rate;
+ if (slot.m_envelope_gen.m_volume >= (0x3ff << EG_SHIFT))
+ {
+ slot.m_envelope_gen.m_state = state_t::DECAY1;
+ if (slot.m_envelope_gen.m_decay1_rate >= (0x400 << EG_SHIFT)) //Skip DECAY1, go directly to DECAY2
+ {
+ slot.m_envelope_gen.m_state = state_t::DECAY2;
+ }
+ slot.m_envelope_gen.m_volume = 0x3ff << EG_SHIFT;
+ }
+ break;
+ case state_t::DECAY1:
+ slot.m_envelope_gen.m_volume -= slot.m_envelope_gen.m_decay1_rate;
+ if (slot.m_envelope_gen.m_volume <= 0)
+ {
+ slot.m_envelope_gen.m_volume = 0;
+ }
+ if (slot.m_envelope_gen.m_volume >> (EG_SHIFT + 6) <= slot.m_envelope_gen.m_decay_level)
+ {
+ slot.m_envelope_gen.m_state = state_t::DECAY2;
+ }
+ break;
+ case state_t::DECAY2:
+ slot.m_envelope_gen.m_volume -= slot.m_envelope_gen.m_decay2_rate;
+ if (slot.m_envelope_gen.m_volume <= 0)
+ {
+ slot.m_envelope_gen.m_volume = 0;
+ }
+ break;
+ case state_t::RELEASE:
+ slot.m_envelope_gen.m_volume -= slot.m_envelope_gen.m_release_rate;
+ if (slot.m_envelope_gen.m_volume <= 0)
+ {
+ slot.m_envelope_gen.m_volume = 0;
+ slot.m_playing = false;
+ }
+ break;
+ default:
+ return 1 << TL_SHIFT;
+ }
+
+ // TODO: this is currently only implemented for GEW7, it's probably not accurate
+ if (slot.m_envelope_gen.m_reverb && slot.m_envelope_gen.m_state != state_t::ATTACK
+ && (slot.m_envelope_gen.m_volume >> EG_SHIFT) <= 0x300)
+ {
+ slot.m_envelope_gen.m_decay1_rate = m_decay_release_step[17];
+ slot.m_envelope_gen.m_decay2_rate = m_decay_release_step[17];
+ slot.m_envelope_gen.m_release_rate = m_decay_release_step[17];
+ }
+
+ return m_linear_to_exp_volume[slot.m_envelope_gen.m_volume >> EG_SHIFT];
+}
+
+uint32_t gew_pcm_device::get_rate(uint32_t *steps, int32_t rate, uint32_t val)
+{
+ if (val == 0)
+ {
+ return steps[0];
+ }
+ if (val == 0xf)
+ {
+ return steps[0x3f];
+ }
+
+ const int r = std::clamp(4 * (int)val + rate, 0, 0x3f);
+ return steps[r];
+}
+
+void gew_pcm_device::envelope_generator_calc(slot_t &slot)
+{
+ int32_t octave = slot.m_octave;
+ if (octave & 8) {
+ octave = octave - 16;
+ }
+
+ int32_t rate;
+ if (slot.m_sample.m_key_rate_scale != 0xf)
+ {
+ rate = (octave + slot.m_sample.m_key_rate_scale) * 2 + BIT(slot.m_pitch, 9);
+ }
+ else
+ {
+ rate = 0;
+ }
+
+ slot.m_envelope_gen.m_attack_rate = get_rate(m_attack_step.get(), rate, slot.m_sample.m_attack_reg);
+ slot.m_envelope_gen.m_decay1_rate = get_rate(m_decay_release_step.get(), rate, slot.m_sample.m_decay1_reg);
+ slot.m_envelope_gen.m_decay2_rate = get_rate(m_decay_release_step.get(), rate, slot.m_sample.m_decay2_reg);
+ slot.m_envelope_gen.m_release_rate = get_rate(m_decay_release_step.get(), rate, slot.m_sample.m_release_reg);
+ slot.m_envelope_gen.m_decay_level = 0xf - slot.m_sample.m_decay_level;
+ slot.m_envelope_gen.m_reverb = false;
+}
+
+/*****************************
+ LFO SECTION
+*****************************/
+
+constexpr uint32_t gew_pcm_device::LFO_SHIFT;
+
+const float gew_pcm_device::LFO_FREQ[8] = // In Hertz
+{
+ 0.168f,
+ 2.019f,
+ 3.196f,
+ 4.206f,
+ 5.215f,
+ 5.888f,
+ 6.224f,
+ 7.066f
+};
+
+const float gew_pcm_device::PHASE_SCALE_LIMIT[8] = // In Cents
+{
+ 0.0f,
+ 3.378f,
+ 5.065f,
+ 6.750f,
+ 10.114f,
+ 20.170f,
+ 40.180f,
+ 79.307f
+};
+
+const float gew_pcm_device::AMPLITUDE_SCALE_LIMIT[8] = // In Decibels
+{
+ 0.0f,
+ 0.4f,
+ 0.8f,
+ 1.5f,
+ 3.0f,
+ 6.0f,
+ 12.0f,
+ 24.0f
+};
+
+void gew_pcm_device::lfo_init()
+{
+ m_pitch_table = make_unique_clear<int32_t[]>(256);
+ m_amplitude_table = make_unique_clear<int32_t[]>(256);
+ for (int32_t i = 0; i < 256; ++i)
+ {
+ if (i < 64)
+ {
+ m_pitch_table[i] = i * 2 + 128;
+ }
+ else if (i < 128)
+ {
+ m_pitch_table[i] = 383 - i * 2;
+ }
+ else if (i < 192)
+ {
+ m_pitch_table[i] = 384 - i * 2;
+ }
+ else
+ {
+ m_pitch_table[i] = i * 2 - 383;
+ }
+
+ if (i < 128)
+ {
+ m_amplitude_table[i] = 255 - (i * 2);
+ }
+ else
+ {
+ m_amplitude_table[i] = (i * 2) - 256;
+ }
+ }
+
+ for (int32_t table = 0; table < 8; ++table)
+ {
+ float limit = PHASE_SCALE_LIMIT[table];
+ m_pitch_scale_tables[table] = make_unique_clear<int32_t[]>(256);
+ for (int32_t i = -128; i < 128; ++i)
+ {
+ const float value = (limit * (float)i) / 128.0f;
+ const float converted = powf(2.0f, value / 1200.0f);
+ m_pitch_scale_tables[table][i + 128] = value_to_fixed(LFO_SHIFT, converted);
+ }
+
+ limit = -AMPLITUDE_SCALE_LIMIT[table];
+ m_amplitude_scale_tables[table] = make_unique_clear<int32_t[]>(256);
+ for (int32_t i = 0; i < 256; ++i)
+ {
+ const float value = (limit * (float)i) / 256.0f;
+ const float converted = powf(10.0f, value / 20.0f);
+ m_amplitude_scale_tables[table][i] = value_to_fixed(LFO_SHIFT, converted);
+ }
+ }
+}
+
+uint32_t gew_pcm_device::value_to_fixed(const uint32_t bits, const float value)
+{
+ const float float_shift = float(1 << bits);
+ return uint32_t(float_shift * value);
+}
+
+int32_t gew_pcm_device::pitch_lfo_step(lfo_t &lfo)
+{
+ lfo.m_phase += lfo.m_phase_step;
+ int32_t p = lfo.m_table[(lfo.m_phase >> LFO_SHIFT) & 0xff];
+ p = lfo.m_scale[p];
+ return p << (TL_SHIFT - LFO_SHIFT);
+}
+
+int32_t gew_pcm_device::amplitude_lfo_step(lfo_t &lfo)
+{
+ lfo.m_phase += lfo.m_phase_step;
+ int32_t p = lfo.m_table[(lfo.m_phase >> LFO_SHIFT) & 0xff];
+ p = lfo.m_scale[p];
+ return p << (TL_SHIFT - LFO_SHIFT);
+}
+
+void gew_pcm_device::lfo_compute_step(lfo_t &lfo, uint32_t lfo_frequency, uint32_t lfo_scale, int32_t amplitude_lfo)
+{
+ float step = (float)LFO_FREQ[lfo_frequency] * 256.0f / (float)m_rate;
+ lfo.m_phase_step = uint32_t(float(1 << LFO_SHIFT) * step);
+ if (amplitude_lfo)
+ {
+ lfo.m_table = m_amplitude_table.get();
+ lfo.m_scale = m_amplitude_scale_tables[lfo_scale].get();
+ }
+ else
+ {
+ lfo.m_table = m_pitch_table.get();
+ lfo.m_scale = m_pitch_scale_tables[lfo_scale].get();
+ }
+}
+
+/* MAME access functions */
+
+gew_pcm_device::gew_pcm_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock,
+ uint32_t voices, uint32_t clock_divider) :
+ device_t(mconfig, type, tag, owner, clock),
+ device_sound_interface(mconfig, *this),
+ device_rom_interface(mconfig, *this),
+ m_stream(nullptr),
+ m_slots(nullptr),
+ m_rate(0),
+ m_voices(voices),
+ m_clock_divider(clock_divider),
+ m_attack_step(nullptr),
+ m_decay_release_step(nullptr),
+ m_freq_step_table(nullptr),
+ m_left_pan_table(nullptr),
+ m_right_pan_table(nullptr),
+ m_linear_to_exp_volume(nullptr),
+ m_total_level_steps(nullptr)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void gew_pcm_device::device_start()
+{
+ m_rate = (float)clock() / m_clock_divider;
+
+ m_stream = stream_alloc(0, 2, m_rate);
+
+ // Volume + pan table
+ m_left_pan_table = make_unique_clear<int32_t[]>(0x800);
+ m_right_pan_table = make_unique_clear<int32_t[]>(0x800);
+ for (int32_t level = 0; level < 0x80; ++level)
+ {
+ const float vol_db = (float)level * (-24.0f) / 64.0f;
+ const float total_level = powf(10.0f, vol_db / 20.0f) / 4.0f;
+
+ for (int32_t pan = 0; pan < 0x10; ++pan)
+ {
+ float pan_left, pan_right;
+ if (pan == 0x8)
+ {
+ pan_left = 0.0;
+ pan_right = 0.0;
+ }
+ else if (pan == 0x0)
+ {
+ pan_left = 1.0;
+ pan_right = 1.0;
+ }
+ else if (pan & 0x8)
+ {
+ pan_left = 1.0;
+
+ const int32_t inverted_pan = 0x10 - pan;
+ const float pan_vol_db = (float)inverted_pan * (-12.0f) / 4.0f;
+
+ pan_right = pow(10.0f, pan_vol_db / 20.0f);
+
+ if ((inverted_pan & 0x7) == 7)
+ {
+ pan_right = 0.0;
+ }
+ }
+ else
+ {
+ pan_right = 1.0;
+
+ const float pan_vol_db = (float)pan * (-12.0f) / 4.0f;
+
+ pan_left = pow(10.0f, pan_vol_db / 20.0f);
+
+ if ((pan & 0x7) == 7)
+ {
+ pan_left = 0.0;
+ }
+ }
+
+ m_left_pan_table[(pan << 7) | level] = value_to_fixed(TL_SHIFT, pan_left * total_level);
+ m_right_pan_table[(pan << 7) | level] = value_to_fixed(TL_SHIFT, pan_right * total_level);
+ }
+ }
+
+ // Pitch steps
+ m_freq_step_table = make_unique_clear<uint32_t[]>(0x400);
+ for (int32_t i = 0; i < 0x400; ++i)
+ {
+ const float fcent = m_rate * (1024.0f + (float)i) / 1024.0f;
+ m_freq_step_table[i] = value_to_fixed(TL_SHIFT, fcent);
+ }
+
+ // Envelope steps
+ m_attack_step = make_unique_clear<uint32_t[]>(0x40);
+ m_decay_release_step = make_unique_clear<uint32_t[]>(0x40);
+ envelope_generator_init(BASE_TIMES, 14.32833);
+
+ // Total level interpolation steps
+ m_total_level_steps = make_unique_clear<int32_t[]>(2);
+ m_total_level_steps[0] = -(float)(0x80 << TL_SHIFT) / (78.2f * 44100.0f / 1000.0f); // lower
+ m_total_level_steps[1] = (float)(0x80 << TL_SHIFT) / (78.2f * 2 * 44100.0f / 1000.0f); // raise
+
+ // build the linear->exponential ramps
+ m_linear_to_exp_volume = make_unique_clear<int32_t[]>(0x400);
+ for (int32_t i = 0; i < 0x400; ++i)
+ {
+ const float db = -(96.0f - (96.0f * (float)i / (float)0x400));
+ const float exp_volume = powf(10.0f, db / 20.0f);
+ m_linear_to_exp_volume[i] = value_to_fixed(TL_SHIFT, exp_volume);
+ }
+
+ // Slots
+ m_slots = std::make_unique<slot_t[]>(m_voices);
+
+ save_pointer(STRUCT_MEMBER(m_slots, m_regs), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_playing), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_offset), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_octave), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_pitch), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_step), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_reverse), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_pan), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_total_level), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_dest_total_level), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_total_level_step), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_prev_sample), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_lfo_frequency), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_vibrato), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_tremolo), m_voices);
+
+ for (int32_t slot = 0; slot < m_voices; ++slot)
+ {
+ save_item(NAME(m_slots[slot].m_sample.m_start), slot);
+ save_item(NAME(m_slots[slot].m_sample.m_loop), slot);
+ save_item(NAME(m_slots[slot].m_sample.m_end), slot);
+ save_item(NAME(m_slots[slot].m_sample.m_attack_reg), slot);
+ save_item(NAME(m_slots[slot].m_sample.m_decay1_reg), slot);
+ save_item(NAME(m_slots[slot].m_sample.m_decay2_reg), slot);
+ save_item(NAME(m_slots[slot].m_sample.m_decay_level), slot);
+ save_item(NAME(m_slots[slot].m_sample.m_release_reg), slot);
+ save_item(NAME(m_slots[slot].m_sample.m_key_rate_scale), slot);
+ save_item(NAME(m_slots[slot].m_sample.m_lfo_vibrato_reg), slot);
+ save_item(NAME(m_slots[slot].m_sample.m_lfo_amplitude_reg), slot);
+ save_item(NAME(m_slots[slot].m_sample.m_format), slot);
+
+ save_item(NAME(m_slots[slot].m_envelope_gen.m_volume), slot);
+ save_item(NAME(m_slots[slot].m_envelope_gen.m_state), slot);
+ save_item(NAME(m_slots[slot].m_envelope_gen.m_reverb), slot);
+ save_item(NAME(m_slots[slot].m_envelope_gen.step), slot);
+ save_item(NAME(m_slots[slot].m_envelope_gen.m_attack_rate), slot);
+ save_item(NAME(m_slots[slot].m_envelope_gen.m_decay1_rate), slot);
+ save_item(NAME(m_slots[slot].m_envelope_gen.m_decay2_rate), slot);
+ save_item(NAME(m_slots[slot].m_envelope_gen.m_release_rate), slot);
+ save_item(NAME(m_slots[slot].m_envelope_gen.m_decay_level), slot);
+
+ save_item(NAME(m_slots[slot].m_pitch_lfo.m_phase), slot);
+ save_item(NAME(m_slots[slot].m_pitch_lfo.m_phase_step), slot);
+ save_item(NAME(m_slots[slot].m_amplitude_lfo.m_phase), slot);
+ save_item(NAME(m_slots[slot].m_amplitude_lfo.m_phase_step), slot);
+ }
+
+ lfo_init();
+}
+
+void gew_pcm_device::device_reset()
+{
+ for (int32_t slot = 0; slot < m_voices; ++slot)
+ {
+ m_slots[slot].m_playing = false;
+ }
+}
+
+//-------------------------------------------------
+// device_clock_changed - called if the clock
+// changes
+//-------------------------------------------------
+
+void gew_pcm_device::device_clock_changed()
+{
+ m_rate = (float)clock() / m_clock_divider;
+ m_stream->set_sample_rate(m_rate);
+
+ for (int32_t i = 0; i < 0x400; ++i)
+ {
+ const float fcent = m_rate * (1024.0f + (float)i) / 1024.0f;
+ m_freq_step_table[i] = value_to_fixed(TL_SHIFT, fcent);
+ }
+}
+
+//-----------------------------------------------------
+// dump_sample - dump current sample to WAV file
+//-----------------------------------------------------
+
+#if MULTIPCM_LOG_SAMPLES
+void gew_pcm_device::dump_sample(slot_t &slot)
+{
+ if (m_logged_map[slot.m_sample.m_start])
+ return;
+
+ m_logged_map[slot.m_sample.m_start] = true;
+
+ char filebuf[256];
+ snprintf(filebuf, 256, "multipcm%08x.wav", slot.m_sample.m_start);
+ util::wav_file_ptr file = util::wav_open(filebuf, m_stream->sample_rate(), 1);
+ if (file == nullptr)
+ return;
+
+ uint32_t offset = slot.m_offset;
+ bool done = false;
+ while (!done)
+ {
+ int16_t sample = (int16_t)(read_byte(slot.m_sample.m_start + (offset >> TL_SHIFT)) << 8);
+ util::wav_add_data_16(*file.get(), &sample, 1);
+
+ offset += 1 << TL_SHIFT;
+ if (offset >= (slot.m_sample.m_end << TL_SHIFT))
+ {
+ done = true;
+ }
+ }
+
+ util::wav_close(file.get());
+}
+#endif
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void gew_pcm_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
+{
+ for (int32_t i = 0; i < outputs[0].samples(); ++i)
+ {
+ int32_t smpl = 0;
+ int32_t smpr = 0;
+ for (int32_t sl = 0; sl < m_voices; ++sl)
+ {
+ slot_t& slot = m_slots[sl];
+ if (slot.m_playing)
+ {
+ uint32_t vol = (slot.m_total_level >> TL_SHIFT) | (slot.m_pan << 7);
+ uint32_t spos = slot.m_offset >> TL_SHIFT;
+ uint32_t step = slot.m_step;
+ int32_t csample = 0;
+ int32_t fpart = slot.m_offset & ((1 << TL_SHIFT) - 1);
+
+ if (slot.m_reverse)
+ {
+ spos = slot.m_sample.m_end - spos - 1;
+ }
+
+ if (slot.m_sample.m_format & 4) // 12-bit linear
+ {
+ offs_t adr = slot.m_sample.m_start + (spos >> 1) * 3;
+ if (!(spos & 1))
+ { // ab.c ..
+ s16 w0 = read_byte(adr) << 8 | ((read_byte(adr + 1) & 0xf) << 4);
+ csample = w0;
+ }
+ else
+ { // ..C. AB
+ s16 w0 = (read_byte(adr + 2) << 8) | (read_byte(adr + 1) & 0xf0);
+ csample = w0;
+ }
+ }
+ else
+ {
+ csample = (int16_t)(read_byte(slot.m_sample.m_start + spos) << 8);
+ }
+
+ int32_t sample = (csample * fpart + slot.m_prev_sample * ((1 << TL_SHIFT) - fpart)) >> TL_SHIFT;
+
+ if (slot.m_vibrato) // Vibrato enabled
+ {
+ step = step * pitch_lfo_step(slot.m_pitch_lfo);
+ step >>= TL_SHIFT;
+ }
+
+ slot.m_offset += step;
+
+ if (spos ^ (slot.m_offset >> TL_SHIFT))
+ {
+ slot.m_prev_sample = csample;
+ }
+
+ if (slot.m_offset >= (slot.m_sample.m_end << TL_SHIFT))
+ {
+ slot.m_offset -= (slot.m_sample.m_end - slot.m_sample.m_loop) << TL_SHIFT;
+ // DD-9 expects the looped silence at the end of some samples to be the same whether reversed or not
+ slot.m_reverse = false;
+ }
+
+ if ((slot.m_total_level >> TL_SHIFT) != slot.m_dest_total_level)
+ {
+ slot.m_total_level += slot.m_total_level_step;
+ }
+
+ if (slot.m_tremolo) // Tremolo enabled
+ {
+ sample = sample * amplitude_lfo_step(slot.m_amplitude_lfo);
+ sample >>= TL_SHIFT;
+ }
+
+ sample = (sample * envelope_generator_update(slot)) >> 10;
+
+ smpl += (m_left_pan_table[vol] * sample) >> TL_SHIFT;
+ smpr += (m_right_pan_table[vol] * sample) >> TL_SHIFT;
+ }
+ }
+
+ outputs[0].put_int_clamp(i, smpl, 32768);
+ outputs[1].put_int_clamp(i, smpr, 32768);
+ }
+}
+
+
+//-------------------------------------------------
+// rom_bank_pre_change - refresh the stream if the
+// ROM banking changes
+//-------------------------------------------------
+
+void gew_pcm_device::rom_bank_pre_change()
+{
+ m_stream->update();
+}
diff --git a/src/devices/sound/gew.h b/src/devices/sound/gew.h
new file mode 100644
index 00000000000..75a3f57f51f
--- /dev/null
+++ b/src/devices/sound/gew.h
@@ -0,0 +1,160 @@
+// license:BSD-3-Clause
+// copyright-holders:Miguel Angel Horna
+#ifndef MAME_SOUND_GEW_H
+#define MAME_SOUND_GEW_H
+
+#pragma once
+
+#include "dirom.h"
+
+#define MULTIPCM_LOG_SAMPLES 0
+
+#if MULTIPCM_LOG_SAMPLES
+#include <map>
+#endif
+
+class gew_pcm_device : public device_t,
+ public device_sound_interface,
+ public device_rom_interface<22, 0, 0, ENDIANNESS_BIG>
+{
+public:
+ static constexpr feature_type imperfect_features() { return feature::SOUND; }
+
+protected:
+ gew_pcm_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock,
+ uint32_t voices, uint32_t clock_divider);
+
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_clock_changed() override;
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
+
+ // device_rom_interface overrides
+ virtual void rom_bank_pre_change() override;
+
+ struct sample_t
+ {
+ uint32_t m_start = 0;
+ uint32_t m_loop = 0;
+ uint32_t m_end = 0;
+ uint8_t m_attack_reg = 0;
+ uint8_t m_decay1_reg = 0;
+ uint8_t m_decay2_reg = 0;
+ uint8_t m_decay_level = 0;
+ uint8_t m_release_reg = 0;
+ uint8_t m_key_rate_scale = 0;
+ uint8_t m_lfo_vibrato_reg = 0;
+ uint8_t m_lfo_amplitude_reg = 0;
+ uint8_t m_format = 0;
+ };
+
+ enum class state_t : u8
+ {
+ ATTACK,
+ DECAY1,
+ DECAY2,
+ RELEASE
+ };
+
+ struct envelope_gen_t
+ {
+ int32_t m_volume = 0;
+ state_t m_state = state_t::ATTACK;
+ uint8_t m_reverb = 0;
+ int32_t step = 0;
+ //step vals
+ int32_t m_attack_rate = 0; // Attack
+ int32_t m_decay1_rate = 0; // Decay1
+ int32_t m_decay2_rate = 0; // Decay2
+ int32_t m_release_rate = 0; // Release
+ int32_t m_decay_level = 0; // Decay level
+ };
+
+ struct lfo_t
+ {
+ uint16_t m_phase = 0;
+ uint32_t m_phase_step = 0;
+ int32_t *m_table = nullptr;
+ int32_t *m_scale = nullptr;
+ };
+
+ struct slot_t
+ {
+ uint8_t m_regs[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ bool m_playing = false;
+ sample_t m_sample;
+ uint32_t m_offset = 0;
+ uint8_t m_octave = 0;
+ uint16_t m_pitch = 0;
+ uint32_t m_step = 0;
+ bool m_reverse = false;
+ uint32_t m_pan = 0;
+ uint32_t m_total_level = 0;
+ uint32_t m_dest_total_level = 0;
+ int32_t m_total_level_step = 0;
+ int32_t m_prev_sample = 0;
+ envelope_gen_t m_envelope_gen;
+ uint8_t m_lfo_frequency;
+ lfo_t m_pitch_lfo; // Pitch lfo
+ uint8_t m_vibrato = 0;
+ lfo_t m_amplitude_lfo; // AM lfo
+ uint8_t m_tremolo = 0;
+ };
+
+ uint32_t value_to_fixed(const uint32_t bits, const float value);
+
+ void retrigger_sample(slot_t &slot);
+ void update_step(slot_t &slot);
+
+ static constexpr uint32_t TL_SHIFT = 12;
+ static constexpr uint32_t EG_SHIFT = 16;
+ static constexpr uint32_t LFO_SHIFT = 8;
+
+ // Internal LFO functions
+ void lfo_init();
+ void lfo_compute_step(lfo_t &lfo, uint32_t lfo_frequency, uint32_t LFOS, int32_t amplitude_lfo);
+ int32_t pitch_lfo_step(lfo_t &lfo);
+ int32_t amplitude_lfo_step(lfo_t &lfo);
+
+ // Internal envelope functions
+ void envelope_generator_init(const double (&rates)[64], double attack_decay_ratio);
+ int32_t envelope_generator_update(slot_t &slot);
+ void envelope_generator_calc(slot_t &slot);
+ uint32_t get_rate(uint32_t *steps, int32_t rate, uint32_t val);
+
+#if MULTIPCM_LOG_SAMPLES
+ void dump_sample(slot_t &slot);
+ std::map<uint32_t, bool> m_logged_map;
+#endif
+
+ static const double BASE_TIMES[64];
+
+ static const float LFO_FREQ[8];
+ static const float PHASE_SCALE_LIMIT[8];
+ static const float AMPLITUDE_SCALE_LIMIT[8];
+
+ // internal state
+ sound_stream* m_stream;
+ std::unique_ptr<slot_t[]> m_slots;
+ float m_rate;
+ uint32_t m_voices;
+ uint32_t m_clock_divider;
+
+ std::unique_ptr<uint32_t[]> m_attack_step;
+ std::unique_ptr<uint32_t[]> m_decay_release_step; // Envelope step tables
+ std::unique_ptr<uint32_t[]> m_freq_step_table; // Frequency step table
+
+ std::unique_ptr<int32_t[]> m_left_pan_table;
+ std::unique_ptr<int32_t[]> m_right_pan_table;
+ std::unique_ptr<int32_t[]> m_linear_to_exp_volume;
+ std::unique_ptr<int32_t[]> m_total_level_steps;
+
+ std::unique_ptr<int32_t[]> m_pitch_table;
+ std::unique_ptr<int32_t[]> m_pitch_scale_tables[8];
+ std::unique_ptr<int32_t[]> m_amplitude_table;
+ std::unique_ptr<int32_t[]> m_amplitude_scale_tables[8];
+};
+
+#endif // MAME_SOUND_GEW_H
diff --git a/src/devices/sound/gew7.cpp b/src/devices/sound/gew7.cpp
new file mode 100644
index 00000000000..52859f86ee4
--- /dev/null
+++ b/src/devices/sound/gew7.cpp
@@ -0,0 +1,206 @@
+// license:BSD-3-Clause
+// copyright-holders:Devin Acker
+
+#include "emu.h"
+#include "gew7.h"
+
+DEFINE_DEVICE_TYPE(GEW7_PCM, gew7_pcm_device, "gew7_pcm", "Yamaha GEW7 PCM")
+
+gew7_pcm_device::gew7_pcm_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) :
+ gew_pcm_device(mconfig, GEW7_PCM, tag, owner, clock, 12, 72)
+{
+}
+
+void gew7_pcm_device::device_start()
+{
+ gew_pcm_device::device_start();
+
+ // fudge the default envelope step values to make them more like how they sound on this chip
+ // TODO: get some measurements from real hardware and try to make this more accurate
+ static const unsigned steps[] = { 0x99, 0xaa, 0xcc, 0x100 };
+
+ for (int32_t i = 4; i < 0x40; ++i)
+ {
+ m_attack_step[i] = steps[i % 4] << (i / 4);
+ m_decay_release_step[i] = m_attack_step[i] >> 5;
+ }
+ m_attack_step[0x3f] = 0x400 << EG_SHIFT;
+
+ // stereo channels are reversed compared to GEW8
+ std::swap(m_left_pan_table, m_right_pan_table);
+}
+
+void gew7_pcm_device::init_sample(sample_t& sample, uint32_t index)
+{
+ uint32_t address = 0x4000 | index * 12;
+
+ // format & address parts are mostly the same as GEW8
+ sample.m_start = (read_byte(address) << 16) | (read_byte(address + 1) << 8) | read_byte(address + 2);
+ sample.m_format = (sample.m_start >> 20) & 0xfe;
+ sample.m_start &= 0x1fffff;
+ sample.m_loop = (read_byte(address + 3) << 8) | read_byte(address + 4);
+ sample.m_end = 0x4000 - ((read_byte(address + 5) << 8) | read_byte(address + 6));
+
+ // TODO: bytes 7 & 8 control LFO, reverse playback, etc (verify the details)
+ // the LFO bits are laid out a little differently than they are for GEW8
+ sample.m_lfo_vibrato_reg = read_byte(address + 7);
+ sample.m_lfo_amplitude_reg = read_byte(address + 8);
+
+ // ADSR bits are arranged differently compared to GEW8
+ sample.m_attack_reg = read_byte(address + 9) >> 4;
+ sample.m_decay1_reg = read_byte(address + 9) & 0xf;
+ sample.m_decay2_reg = read_byte(address + 10) >> 4;
+ sample.m_release_reg = read_byte(address + 10) & 0xf;
+ sample.m_key_rate_scale = read_byte(address + 11) >> 4;
+ sample.m_decay_level = read_byte(address + 11) & 0xf;
+}
+
+uint8_t gew7_pcm_device::read(offs_t offset)
+{
+ if (offset >= 0x60) return 0;
+
+ if (!machine().side_effects_disabled())
+ m_stream->update();
+
+ slot_t& slot = m_slots[offset >> 3];
+ const uint8_t reg = offset & 7;
+
+ switch (reg)
+ {
+ case 3:
+ // at least some GEW7-based keyboards seem to expect the key-on flag to go off by itself
+ // for certain "one-shot" sounds (i.e. the beeps when changing volume/tempo)
+ if (!slot.m_playing)
+ return slot.m_regs[reg] & 0x7f;
+ break;
+
+ case 6:
+ // uppermost bits of current envelope level (inverted)
+ if (!slot.m_playing)
+ return 0xff;
+
+ return ~slot.m_envelope_gen.m_volume >> (EG_SHIFT + 2);
+
+ case 7:
+ // next 2 lower bits of envelope level (inverted) + 2 bits of envelope state
+ if (!slot.m_playing)
+ return 0xf0;
+
+ return ((~slot.m_envelope_gen.m_volume >> EG_SHIFT) << 6)
+ | ((uint8_t)slot.m_envelope_gen.m_state << 4);
+ }
+
+ return slot.m_regs[reg];
+}
+
+void gew7_pcm_device::write(offs_t offset, uint8_t data)
+{
+ if (offset >= 0x60) return;
+
+ m_stream->update();
+
+ const uint8_t voice = offset >> 3;
+ const uint8_t reg = offset & 7;
+ slot_t& slot = m_slots[voice];
+ slot.m_regs[reg] = data;
+
+ switch (reg)
+ {
+ case 0: // sample
+ init_sample(slot.m_sample, slot.m_regs[0] | ((slot.m_regs[1] & 1) << 8));
+
+ slot.m_lfo_frequency = slot.m_sample.m_lfo_amplitude_reg & 7;
+ write_hi((voice << 2) | 3, slot.m_sample.m_lfo_vibrato_reg);
+
+ // retrigger if key is on
+ if (slot.m_playing)
+ retrigger_sample(slot);
+ break;
+
+ case 1: // pitch
+ case 2:
+ slot.m_octave = slot.m_regs[2] >> 4;
+ slot.m_pitch = ((slot.m_regs[2] & 0xf) << 6) | (slot.m_regs[1] >> 2);
+ update_step(slot);
+ // adjust pitch step for oversampling
+ slot.m_step >>= 1;
+ break;
+
+ case 3: // key on + reverb + panpot
+ if (data & 0x80)
+ {
+ slot.m_playing = true;
+ retrigger_sample(slot);
+ }
+ else if (slot.m_playing)
+ {
+ slot.m_envelope_gen.m_state = state_t::RELEASE;
+ }
+
+ slot.m_envelope_gen.m_reverb = BIT(data, 4);
+ slot.m_pan = data & 0xf;
+ break;
+
+ case 4: // TL
+ slot.m_dest_total_level = data & 0x7f;
+ slot.m_total_level = slot.m_dest_total_level << TL_SHIFT;
+ break;
+ }
+}
+
+uint8_t gew7_pcm_device::read_hi(offs_t offset)
+{
+ if (offset >= 0x30) return 0;
+
+ if (!machine().side_effects_disabled())
+ m_stream->update();
+
+ slot_t& slot = m_slots[offset >> 2];
+ switch (offset & 3)
+ {
+ case 0: // TODO: unverified, just a guess based on reg 1
+ return (slot.m_sample.m_attack_reg << 4) | slot.m_sample.m_decay1_reg;
+
+ case 1: // TODO: unverified, but gets ORed with 0x0f when killing a voice before waiting for envelope to end
+ return (slot.m_sample.m_decay2_reg << 4) | slot.m_sample.m_release_reg;
+
+ case 3:
+ return (slot.m_tremolo << 3) | slot.m_vibrato;
+ }
+
+ return 0;
+}
+
+void gew7_pcm_device::write_hi(offs_t offset, uint8_t data)
+{
+ if (offset >= 0x30) return;
+
+ m_stream->update();
+
+ slot_t& slot = m_slots[offset >> 2];
+ switch (offset & 3)
+ {
+ case 0:
+ slot.m_sample.m_attack_reg = data >> 4;
+ slot.m_sample.m_decay1_reg = data & 0xf;
+ envelope_generator_calc(slot);
+ break;
+
+ case 1:
+ slot.m_sample.m_decay2_reg = data >> 4;
+ slot.m_sample.m_release_reg = data & 0xf;
+ envelope_generator_calc(slot);
+ break;
+
+ case 2: // TODO: ??? (probably related to unknown bits of sample table)
+ break;
+
+ case 3:
+ slot.m_vibrato = data & 7;
+ slot.m_tremolo = (data >> 3) & 7;
+ slot.m_reverse = data >> 7;
+ lfo_compute_step(slot.m_pitch_lfo, slot.m_lfo_frequency, slot.m_vibrato, 0);
+ lfo_compute_step(slot.m_amplitude_lfo, slot.m_lfo_frequency, slot.m_tremolo, 1);
+ break;
+ }
+}
diff --git a/src/devices/sound/gew7.h b/src/devices/sound/gew7.h
new file mode 100644
index 00000000000..5d50b2872f4
--- /dev/null
+++ b/src/devices/sound/gew7.h
@@ -0,0 +1,30 @@
+// license:BSD-3-Clause
+// copyright-holders:Devin Acker
+#ifndef MAME_SOUND_GEW7_H
+#define MAME_SOUND_GEW7_H
+
+#pragma once
+
+#include "gew.h"
+
+class gew7_pcm_device : public gew_pcm_device
+{
+public:
+ gew7_pcm_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock);
+
+ void write(offs_t offset, uint8_t data);
+ uint8_t read(offs_t offset);
+
+ void write_hi(offs_t offset, uint8_t data);
+ uint8_t read_hi(offs_t offset);
+
+protected:
+ virtual void device_start() override;
+
+private:
+ void init_sample(sample_t& sample, uint32_t index);
+};
+
+DECLARE_DEVICE_TYPE(GEW7_PCM, gew7_pcm_device)
+
+#endif // MAME_SOUND_GEW7_H
diff --git a/src/devices/sound/hc55516.cpp b/src/devices/sound/hc55516.cpp
index 5cd17aa6f12..90d9a38d2a6 100644
--- a/src/devices/sound/hc55516.cpp
+++ b/src/devices/sound/hc55516.cpp
@@ -8,11 +8,13 @@
Harris HC-55532 (sometimes labeled HCI-55532 or HC1-55532) [preliminary]
Motorola MC-3417/MC-34115
Motorola MC-3418
- TODO: research HC-55536 and HC-55564 differences vs HC-55516 (better auto-zeroing, and removal of the encoder offset compensation DAC?)
-
- Driver TODOs:
- /src/mame/audio/exidy440.cpp has its own internal implementation of the MC3417 and MC3418, it should be using this file instead
+ TODO:
+ - see .h file
+ - research HC-55536 and HC-55564 differences vs HC-55516 (better auto-zeroing,
+ and removal of the encoder offset compensation DAC?)
+ - /src/mame/exidy/exidy440_a.cpp has its own internal implementation of the
+ MC3417 and MC3418, it should be using this file instead
*****************************************************************************/
@@ -38,7 +40,7 @@ cvsd_device_base::cvsd_device_base(const machine_config &mconfig, device_type ty
: device_t(mconfig, type, tag, owner, clock)
, device_sound_interface(mconfig, *this)
, m_clock_state_push_cb(*this)
- , m_digin_pull_cb(*this)
+ , m_digin_pull_cb(*this, 1)
, m_digout_push_cb(*this)
, m_active_clock_edge(active_clock_edge)
, m_shiftreg_mask(shiftreg_mask)
@@ -86,7 +88,7 @@ void cvsd_device_base::device_reset()
//m_stream->set_sample_rate(clock());
}*/
-READ_LINE_MEMBER( cvsd_device_base::clock_r )
+int cvsd_device_base::clock_r()
{
// prevent debugger from changing the internal state
if (!machine().side_effects_disabled())
@@ -94,12 +96,12 @@ READ_LINE_MEMBER( cvsd_device_base::clock_r )
return clock_state_r();
}
-WRITE_LINE_MEMBER( cvsd_device_base::mclock_w )
+void cvsd_device_base::mclock_w(int state)
{
clock_w(state);
}
-WRITE_LINE_MEMBER( cvsd_device_base::digin_w )
+void cvsd_device_base::digin_w(int state)
{
digit_w(state);
}
@@ -110,12 +112,12 @@ WRITE_LINE_MEMBER( cvsd_device_base::digin_w )
assert(0);
}*/
-WRITE_LINE_MEMBER( cvsd_device_base::dec_encq_w )
+void cvsd_device_base::dec_encq_w(int state)
{
assert(0);
}
-READ_LINE_MEMBER( cvsd_device_base::digout_r )
+int cvsd_device_base::digout_r()
{
return 0;
}
@@ -142,7 +144,8 @@ inline bool cvsd_device_base::is_active_clock_transition(bool clock_state)
inline bool cvsd_device_base::current_clock_state()
{
// keep track of the clock state given its previous state and the number of samples produced
- // i.e. if we generated m_samples_generated samples, at a sample rate of SAMPLE_RATE, then are we on a positive or negative level of a squarewave at clock() hz? SAMPLE_RATE may not be an integer multiple of clock()
+ // i.e. if we generated m_samples_generated samples, at a sample rate of SAMPLE_RATE, then are we on a
+ // positive or negative level of a squarewave at clock() hz? SAMPLE_RATE may not be an integer multiple of clock()
//uint64_t fractions_of_second = (((uint64_t)m_samples_generated)<<32) / SAMPLE_RATE; // 32.32 bits of seconds passed so far
//uint32_t clock_edges_passed = (fractions_of_second * clock() * 2)>>32
//return (((((uint64_t)m_samples_generated<<32) * clock() * 2 / SAMPLE_RATE)>>32) & 0x1)?true:false;
@@ -221,7 +224,7 @@ hc55516_device::hc55516_device(const machine_config &mconfig, const char *tag, d
hc55516_device::hc55516_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint32_t sylmask, int32_t sylshift, int32_t syladd, int32_t intshift)
: cvsd_device_base(mconfig, type, tag, owner, clock, RISING, 0x7)
, m_agc_push_cb(*this)
- , m_fzq_pull_cb(*this)
+ , m_fzq_pull_cb(*this, 1)
, m_sylmask(sylmask)
, m_sylshift(sylshift)
, m_syladd(syladd)
@@ -240,14 +243,11 @@ hc55516_device::hc55516_device(const machine_config &mconfig, device_type type,
void hc55516_device::device_start()
{
cvsd_device_base::device_start();
+
save_item(NAME(m_sylfilter));
save_item(NAME(m_intfilter));
save_item(NAME(m_agc));
save_item(NAME(m_buffered_fzq));
-
- /* resolve lines */
- m_agc_push_cb.resolve();
- m_fzq_pull_cb.resolve();
}
//-------------------------------------------------
@@ -266,12 +266,12 @@ void hc55516_device::device_reset()
// device specific functions
-WRITE_LINE_MEMBER( hc55516_device::fzq_w )
+void hc55516_device::fzq_w(int state)
{
m_buffered_fzq = state;
}
-READ_LINE_MEMBER( hc55516_device::agc_r )
+int hc55516_device::agc_r()
{
// prevent debugger from changing the internal state
if (!machine().side_effects_disabled())
@@ -289,7 +289,7 @@ void hc55516_device::process_bit(bool bit, bool clock_state)
{
// grab the /FZ state; if the callback is present, use that, otherwise use the buffered state
bool fzq_state = false;
- if (!m_fzq_pull_cb.isnull())
+ if (!m_fzq_pull_cb.isunset())
fzq_state = m_fzq_pull_cb();
else
fzq_state = m_buffered_fzq;
@@ -352,8 +352,7 @@ void hc55516_device::process_bit(bool bit, bool clock_state)
m_agc = true;
// push agc state if a callback is present
- if (!m_agc_push_cb.isnull())
- m_agc_push_cb(m_agc);
+ m_agc_push_cb(m_agc);
}
//-------------------------------------------------
diff --git a/src/devices/sound/hc55516.h b/src/devices/sound/hc55516.h
index 7ae6bd0c14a..e90e7b350ec 100644
--- a/src/devices/sound/hc55516.h
+++ b/src/devices/sound/hc55516.h
@@ -15,19 +15,55 @@ public:
FALLING=false
};
- auto clock_state_cb() { return m_clock_state_push_cb.bind(); } // A clock state change callback. Using this is a bad idea due to lack of synchronization to other devices. TODO: remove this.
- auto digin_cb() { return m_digin_pull_cb.bind(); } // Digital in pull callback function, for use if a clock is specified and we need to pull in the digital in pin state, otherwise unused. TODO: this is not hooked up yet, and should be.
- auto digout_cb() { return m_digout_push_cb.bind(); } // Digital out push callback function. TODO: this is not hooked up or implemented yet, although it is only really relevant for devices which use the CVSD chips in encode mode.
-
- READ_LINE_MEMBER( clock_r ); // Clock pull, really only relevant of something manually polls the clock (and clock is specified), which is a very bad design pattern and will cause synchronization/missed clock transition issues. This function WILL ASSERT if it is called and the clock hz is NOT specified! TODO: remove all use of this, and remove it.
- WRITE_LINE_MEMBER( mclock_w ); // Clock push; this function WILL ASSERT if it is called and the clock hz IS specified!
- WRITE_LINE_MEMBER( digin_w ); // Digital in push to the pin, as a pseudo 'buffer' implemented within the cvsd device itself. This is not technically accurate to hardware, and in the future should be deprecated in favor of digin_cb once the latter is implemented.
- WRITE_LINE_MEMBER( dec_encq_w ); //DEC/ENC decode/encode select push. This is not implemented yet, and relies on an input audio stream. TODO: implement this beyond a do-nothing stub
- READ_LINE_MEMBER( digout_r ); // Digital out pull. TODO: this is not hooked up or implemented yet, although it is only really relevant for devices which use the CVSD chips in encode mode.
- //void audio_in_w(stream_buffer::sample_t data); // Audio In pin, an analog value of the audio waveform being pushed to the chip. TODO: this is not hooked up or implemented yet, and this should really be handled as an input stream from a separate DAC device, not a value push function at all.
- void digit_w(int digit); /* sets the buffered digit (0 or 1), common to all chips. TODO: replace all use of this with digin_cb once implemented */
- void clock_w(int state); /* sets the clock state (0 or 1, clocked on the rising edge), common to all chips */
- virtual int clock_state_r(); /* returns whether the clock is currently LO or HI, common to all chips. TODO: get rid of all use of this, then get rid of it. */
+ // A clock state change callback. Using this is a bad idea due to lack of synchronization to other devices.
+ // TODO: remove this.
+ auto clock_state_cb() { return m_clock_state_push_cb.bind(); }
+
+ // Digital in pull callback function, for use if a clock is specified and we need to pull in the digital
+ // in pin state, otherwise unused. TODO: this is not hooked up yet, and should be.
+ auto digin_cb() { return m_digin_pull_cb.bind(); }
+
+ // Digital out push callback function. TODO: this is not hooked up or implemented yet, although it
+ // is only really relevant for devices which use the CVSD chips in encode mode.
+ auto digout_cb() { return m_digout_push_cb.bind(); }
+
+ // Clock pull, really only relevant of something manually polls the clock (and clock is specified),
+ // which is a very bad design pattern and will cause synchronization/missed clock transition issues.
+ // This function WILL ASSERT if it is called and the clock hz is NOT specified!
+ // TODO: remove all use of this, and remove it.
+ int clock_r();
+
+ // Clock push; this function WILL ASSERT if it is called and the clock hz IS specified!
+ void mclock_w(int state);
+
+ // Digital in push to the pin, as a pseudo 'buffer' implemented within the cvsd device itself.
+ // This is not technically accurate to hardware, and in the future should be deprecated in favor of
+ // digin_cb once the latter is implemented.
+ void digin_w(int state);
+
+ // DEC/ENC decode/encode select push. This is not implemented yet, and relies on an input audio stream.
+ // TODO: implement this beyond a do-nothing stub
+ void dec_encq_w(int state);
+
+ // Digital out pull. TODO: this is not hooked up or implemented yet, although it is only really
+ // relevant for devices which use the CVSD chips in encode mode.
+ int digout_r();
+
+ // Audio In pin, an analog value of the audio waveform being pushed to the chip.
+ // TODO: this is not hooked up or implemented yet, and this should really be handled as an
+ // input stream from a separate DAC device, not a value push function at all.
+ //void audio_in_w(stream_buffer::sample_t data);
+
+ // sets the buffered digit (0 or 1), common to all chips. TODO: replace all use of this with
+ // digin_cb once implemented
+ void digit_w(int digit);
+
+ // sets the clock state (0 or 1, clocked on the rising edge), common to all chips
+ void clock_w(int state);
+
+ // returns whether the clock is currently LO or HI, common to all chips.
+ // TODO: get rid of all use of this, then get rid of it.
+ virtual int clock_state_r();
protected:
cvsd_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, bool active_clock_edge, uint8_t shiftreg_mask);
@@ -41,7 +77,7 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
// callbacks
- devcb_write_line m_clock_state_push_cb; ///TODO: get rid of this, if you use it you should feel bad
+ devcb_write_line m_clock_state_push_cb; // TODO: get rid of this, if you use it you should feel bad
devcb_read_line m_digin_pull_cb;
devcb_write_line m_digout_push_cb;
@@ -51,22 +87,21 @@ protected:
// internal state
sound_stream *m_stream;
- bool m_last_clock_state;
- bool m_buffered_bit;
- uint8_t m_shiftreg;
+ bool m_last_clock_state;
+ bool m_buffered_bit;
+ uint8_t m_shiftreg;
stream_buffer::sample_t m_curr_sample;
stream_buffer::sample_t m_next_sample;
- uint32_t m_samples_generated;
+ uint32_t m_samples_generated;
// specific internal handler overrides, overridden by each chip
virtual void process_bit(bool bit, bool clock_state);
- ///TODO: get rid of these
+ // TODO: get rid of these
inline bool is_external_oscillator();
inline bool is_clock_changed(bool clock_state);
inline bool is_active_clock_transition(bool clock_state);
inline bool current_clock_state();
-
};
@@ -75,14 +110,17 @@ class hc55516_device : public cvsd_device_base
public:
hc55516_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
- auto fzq_cb() { return m_fzq_pull_cb.bind(); } // /FZ (partial reset) pull callback, ok to leave unconnected (we assume it is pulled high)
- auto agc_cb() { return m_agc_push_cb.bind(); } // AGC callback function, called to push the state if the AGC pin changes, ok to leave unconnected
+ // /FZ (partial reset) pull callback, ok to leave unconnected (we assume it is pulled high)
+ auto fzq_cb() { return m_fzq_pull_cb.bind(); }
+
+ // AGC callback function, called to push the state if the AGC pin changes, ok to leave unconnected
+ auto agc_cb() { return m_agc_push_cb.bind(); }
- WRITE_LINE_MEMBER( fzq_w ); // /FZ (partial reset) push
- READ_LINE_MEMBER( agc_r ); // AGC pull
- /* TODO: These are only relevant for encode mode, which isn't done yet! */
- //WRITE_LINE_MEMBER( aptq_w ); // /APT (silence encoder output) push
- //WRITE_LINE_MEMBER( dec_encq_w ); // DEC/ENC decode/encode select push
+ void fzq_w(int state); // /FZ (partial reset) push
+ int agc_r(); // AGC pull
+ // TODO: These are only relevant for encode mode, which isn't done yet!
+ //void aptq_w(int state); // /APT (silence encoder output) push
+ //void dec_encq_w(int state); // DEC/ENC decode/encode select push
protected:
// overridable type for subclass
@@ -144,7 +182,8 @@ protected:
// sound stream update overrides
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
- // const coefficients defined by constructor; should these be adjustable by the user or externally defined, as they are implemented using a set of two small lowpass filters outside the chip?
+ // const coefficients defined by constructor; should these be adjustable by the user or externally defined,
+ // as they are implemented using a set of two small lowpass filters outside the chip?
const double m_charge;
const double m_decay;
const double m_leak;
diff --git a/src/devices/sound/huc6230.cpp b/src/devices/sound/huc6230.cpp
index d257572f79e..ba969479cf2 100644
--- a/src/devices/sound/huc6230.cpp
+++ b/src/devices/sound/huc6230.cpp
@@ -155,7 +155,7 @@ huc6230_device::huc6230_device(const machine_config &mconfig, const char *tag, d
, m_adpcm_freq(0)
, m_pcm_lvol(0)
, m_pcm_rvol(0)
- , m_adpcm_update_cb(*this)
+ , m_adpcm_update_cb(*this, 0)
, m_vca_cb(*this)
{
}
@@ -176,10 +176,6 @@ void huc6230_device::device_add_mconfig(machine_config &config)
void huc6230_device::device_start()
{
- m_adpcm_update_cb.resolve_all_safe(0);
-
- m_vca_cb.resolve_safe();
-
m_stream = stream_alloc(2, 2, clock() / 6);
m_adpcm_timer = timer_alloc(FUNC(huc6230_device::adpcm_timer), this);
diff --git a/src/devices/sound/i5000.h b/src/devices/sound/i5000.h
index abd84f5ac43..cb3e45382e9 100644
--- a/src/devices/sound/i5000.h
+++ b/src/devices/sound/i5000.h
@@ -55,7 +55,6 @@ private:
int vol_l;
int output_r;
int output_l;
-
};
channel_t m_channels[16];
diff --git a/src/devices/sound/ics2115.cpp b/src/devices/sound/ics2115.cpp
index 56c0cf607c4..f38b31e6c86 100644
--- a/src/devices/sound/ics2115.cpp
+++ b/src/devices/sound/ics2115.cpp
@@ -63,8 +63,6 @@ void ics2115_device::device_start()
m_timer[1].timer = timer_alloc(FUNC(ics2115_device::timer_cb_1), this);
m_stream = stream_alloc(0, 2, clock() / (32 * 32));
- m_irq_cb.resolve_safe();
-
//Exact formula as per patent 5809466
//This seems to give the ok fit but it is not good enough.
/*double maxvol = ((1 << volume_bits) - 1) * pow(2., (double)1/0x100);
@@ -434,8 +432,8 @@ int ics2115_device::fill_output(ics2115_voice& voice, std::vector<write_stream_v
{
constexpr int RAMP_SHIFT = 6;
const u32 volacc = (voice.vol.acc >> 14) & 0xfff;
- const u16 vlefti = volacc - m_panlaw[255 - voice.vol.pan]; // left index from acc - pan law
- const u16 vrighti = volacc - m_panlaw[voice.vol.pan]; // right index from acc - pan law
+ const s16 vlefti = volacc - m_panlaw[255 - voice.vol.pan]; // left index from acc - pan law
+ const s16 vrighti = volacc - m_panlaw[voice.vol.pan]; // right index from acc - pan law
//check negative values so no cracks, is it a hardware feature ?
const u16 vleft = vlefti > 0 ? (m_volume[vlefti] * voice.state.ramp >> RAMP_SHIFT) : 0;
const u16 vright = vrighti > 0 ? (m_volume[vrighti] * voice.state.ramp >> RAMP_SHIFT) : 0;
@@ -448,10 +446,9 @@ int ics2115_device::fill_output(ics2115_voice& voice, std::vector<write_stream_v
s32 sample = get_sample(voice);
//15-bit volume + (5-bit worth of 32 channel sum) + 16-bit samples = 4-bit extra
+ //if (voice.playing())
if (!m_vmode || voice.playing())
{
- /*if (voice.playing())
- {*/
outputs[0].add_int(i, (sample * vleft) >> (5 + volume_bits), 32768);
outputs[1].add_int(i, (sample * vright) >> (5 + volume_bits), 32768);
}
@@ -618,7 +615,8 @@ u16 ics2115_device::reg_read()
ret = m_active_osc;
break;
- case 0x0f:{// [osc] Interrupt source/oscillator
+ case 0x0f: // [osc] Interrupt source/oscillator
+ {
ret = 0xff;
for (int i = 0; i <= m_active_osc; i++)
{
@@ -643,7 +641,8 @@ u16 ics2115_device::reg_read()
}
}
ret <<= 8;
- break;}
+ break;
+ }
case 0x10: // [osc] Oscillator Control
ret = voice.osc.ctl << 8;
@@ -1079,10 +1078,9 @@ void ics2115_device::recalc_irq()
//Suspect
bool irq = (m_irq_pending & m_irq_enabled);
for (int i = 0; (!irq) && (i < 32); i++)
- irq |= m_voice[i].vol_ctrl.bitflags.irq_pending && m_voice[i].osc_conf.bitflags.irq_pending;
+ irq |= m_voice[i].vol_ctrl.bitflags.irq_pending && m_voice[i].osc_conf.bitflags.irq_pending;
m_irq_on = irq;
- if (!m_irq_cb.isnull())
- m_irq_cb(irq ? ASSERT_LINE : CLEAR_LINE);
+ m_irq_cb(irq ? ASSERT_LINE : CLEAR_LINE);
}
TIMER_CALLBACK_MEMBER( ics2115_device::timer_cb_0 )
diff --git a/src/devices/sound/iremga20.cpp b/src/devices/sound/iremga20.cpp
index 3ec07d3de35..b87c9f26da0 100644
--- a/src/devices/sound/iremga20.cpp
+++ b/src/devices/sound/iremga20.cpp
@@ -117,10 +117,11 @@ void iremga20_device::device_clock_changed()
}
//-------------------------------------------------
-// rom_bank_updated - the rom bank has changed
+// rom_bank_pre_change - refresh the stream if the
+// ROM banking changes
//-------------------------------------------------
-void iremga20_device::rom_bank_updated()
+void iremga20_device::rom_bank_pre_change()
{
m_stream->update();
}
diff --git a/src/devices/sound/iremga20.h b/src/devices/sound/iremga20.h
index ae60c563d78..99440bc5782 100644
--- a/src/devices/sound/iremga20.h
+++ b/src/devices/sound/iremga20.h
@@ -39,7 +39,7 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
// device_rom_interface overrides
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
private:
struct channel_def
diff --git a/src/devices/sound/k005289.cpp b/src/devices/sound/k005289.cpp
index ffdfb4f0588..4202ce8207c 100644
--- a/src/devices/sound/k005289.cpp
+++ b/src/devices/sound/k005289.cpp
@@ -37,10 +37,6 @@
#include "emu.h"
#include "k005289.h"
-// is this an actual hardware limit? or just an arbitrary divider
-// to bring the output frequency down to a reasonable value for MAME?
-#define CLOCK_DIVIDER 32
-
// device type definition
DEFINE_DEVICE_TYPE(K005289, k005289_device, "k005289", "K005289 SCC")
@@ -72,9 +68,9 @@ void k005289_device::device_start()
m_stream = stream_alloc(0, 1, clock());
/* reset all the voices */
- for (auto & elem : m_voice)
+ for (auto & voice : m_voice)
{
- elem.reset();
+ voice.reset();
}
save_item(STRUCT_MEMBER(m_voice, counter));
@@ -97,7 +93,7 @@ void k005289_device::sound_stream_update(sound_stream &stream, std::vector<read_
for (int i = 0; i < 2; i++)
{
voice_t &v = m_voice[i];
- if ((v.counter--) < 0)
+ if (--v.counter < 0)
{
v.waveform = (v.waveform & ~0x1f) | ((v.waveform + 1) & 0x1f);
v.counter = v.frequency;
diff --git a/src/devices/sound/k005289.h b/src/devices/sound/k005289.h
index cacf1d98f52..618c8dd7ac7 100644
--- a/src/devices/sound/k005289.h
+++ b/src/devices/sound/k005289.h
@@ -34,6 +34,8 @@ private:
struct voice_t
{
+ voice_t() { reset(); }
+
void reset()
{
counter = 0;
@@ -43,11 +45,11 @@ private:
volume = 0;
}
- s16 counter = 0;
- u16 frequency = 0;
- u16 pitch = 0;
- u16 waveform = 0;
- u8 volume = 0;
+ s16 counter;
+ u16 frequency;
+ u16 pitch;
+ u16 waveform;
+ u8 volume;
};
voice_t m_voice[2];
};
diff --git a/src/devices/sound/k007232.cpp b/src/devices/sound/k007232.cpp
index 9b4bc9e4b16..125ebc1352e 100644
--- a/src/devices/sound/k007232.cpp
+++ b/src/devices/sound/k007232.cpp
@@ -70,8 +70,6 @@ void k007232_device::device_start()
space(0).cache(m_cache);
/* Set up the chips */
- m_port_write_handler.resolve_safe();
-
for (int i = 0; i < KDAC_A_PCM_MAX; i++)
{
m_channel[i].addr = 0;
diff --git a/src/devices/sound/k051649.cpp b/src/devices/sound/k051649.cpp
index 5ddffc5f36f..3afa41849d8 100644
--- a/src/devices/sound/k051649.cpp
+++ b/src/devices/sound/k051649.cpp
@@ -1,6 +1,6 @@
// license:BSD-3-Clause
// copyright-holders:Bryan McPhail
-/***************************************************************************
+/*******************************************************************************
Konami 051649 - SCC1 sound as used in Haunted Castle, City Bomber
@@ -12,17 +12,25 @@
waveform from RAM (32 bytes per waveform, 8 bit signed data).
This sound chip is the same as the sound chip in some Konami
- megaROM cartridges for the MSX. It is actually well researched
- and documented:
+ megaROM cartridges for the MSX. This device only emulates the
+ sound portion, not the memory mapper.
- http://bifi.msxnet.org/msxnet/tech/scc.html
+ 052539 is more or less equivalent to this chip except channel 5
+ does not share waveram with channel 4.
- Thanks to Sean Young (sean@mess.org) for some bugfixes.
+ References:
+ - http://bifi.msxnet.org/msxnet/tech/scc.html
+ - http://bifi.msxnet.org/msxnet/tech/soundcartridge
- K052539 is more or less equivalent to this chip except channel 5
- does not share waveram with channel 4.
+ TODO:
+ - make 052539 a subdevice
+ - bus conflicts on 051649 (not 052539). When the CPU accesses waveform RAM
+ and the SCC is reading it at the same time, it can cause audible spikes.
+ A similar thing happens internally when the shared ch4/ch5 do a read at
+ the same time.
+ - test register bits 0-4, not used in any software
-***************************************************************************/
+*******************************************************************************/
#include "emu.h"
#include "k051649.h"
@@ -41,9 +49,9 @@ void k051649_device::scc_map(address_map &map)
DEFINE_DEVICE_TYPE(K051649, k051649_device, "k051649", "K051649 SCC1")
-//**************************************************************************
+//******************************************************************************
// LIVE DEVICE
-//**************************************************************************
+//******************************************************************************
//-------------------------------------------------
// k051649_device - constructor
@@ -72,6 +80,7 @@ void k051649_device::device_start()
save_item(STRUCT_MEMBER(m_channel_list, clock));
save_item(STRUCT_MEMBER(m_channel_list, frequency));
save_item(STRUCT_MEMBER(m_channel_list, volume));
+ save_item(STRUCT_MEMBER(m_channel_list, sample));
save_item(STRUCT_MEMBER(m_channel_list, key));
save_item(STRUCT_MEMBER(m_channel_list, waveram));
save_item(NAME(m_test));
@@ -136,21 +145,25 @@ void k051649_device::sound_stream_update(sound_stream &stream, std::vector<read_
// channel is halted for freq < 9
if (voice.frequency > 8)
{
- if ((voice.clock--) <= 0)
+ if (++voice.clock > voice.frequency)
{
voice.counter = (voice.counter + 1) & 0x1f;
- voice.clock = voice.frequency;
+ voice.clock = 0;
+ }
+ if (voice.clock == 0)
+ {
+ voice.sample = (voice.key ? voice.waveram[voice.counter] : 0) * voice.volume;
}
- // scale to 11 bit digital output on chip
- if (voice.key)
- outputs[0].add_int(i, (voice.waveram[voice.counter] * voice.volume) >> 4, 1024);
}
+
+ // scale to 11 bit digital output on chip
+ outputs[0].add_int(i, voice.sample >> 4, 1024);
}
}
}
-/********************************************************************************/
+/******************************************************************************/
void k051649_device::k051649_waveform_w(offs_t offset, u8 data)
@@ -174,17 +187,19 @@ void k051649_device::k051649_waveform_w(offs_t offset, u8 data)
u8 k051649_device::k051649_waveform_r(offs_t offset)
{
- // test-register bits 6/7 expose the internal counter
+ u8 counter = 0;
+
+ // test register bits 6/7 expose the internal counter
if (m_test & 0xc0)
{
m_stream->update();
- if (offset >= 0x60)
- offset += m_channel_list[3 + (m_test >> 6 & 1)].counter;
+ if (offset >= 0x60 && (m_test & 0xc0) != 0xc0)
+ counter = m_channel_list[3 + (m_test >> 6 & 1)].counter;
else if (m_test & 0x40)
- offset += m_channel_list[offset >> 5].counter;
+ counter = m_channel_list[offset >> 5].counter;
}
- return m_channel_list[offset >> 5].waveram[offset & 0x1f];
+ return m_channel_list[offset >> 5].waveram[(offset + counter) & 0x1f];
}
@@ -201,20 +216,22 @@ void k051649_device::k052539_waveform_w(offs_t offset, u8 data)
u8 k051649_device::k052539_waveform_r(offs_t offset)
{
- // test-register bit 6 exposes the internal counter
+ u8 counter = 0;
+
+ // test register bit 6 exposes the internal counter
if (m_test & 0x40)
{
m_stream->update();
- offset += m_channel_list[offset >> 5].counter;
+ counter = m_channel_list[offset >> 5].counter;
}
- return m_channel_list[offset >> 5].waveram[offset & 0x1f];
+ return m_channel_list[offset >> 5].waveram[(offset + counter) & 0x1f];
}
void k051649_device::k051649_volume_w(offs_t offset, u8 data)
{
m_stream->update();
- m_channel_list[offset & 0x7].volume = data & 0xf;
+ m_channel_list[offset].volume = data & 0xf;
}
@@ -225,21 +242,18 @@ void k051649_device::k051649_frequency_w(offs_t offset, u8 data)
m_stream->update();
- // test-register bit 5 resets the internal counter
- if (m_test & 0x20)
- {
- m_channel_list[offset].counter = 0;
- m_channel_list[offset].clock = 0;
- }
- // TODO: correct?
- else if (m_channel_list[offset].frequency < 9)
- m_channel_list[offset].clock = 0;
-
// update frequency
if (freq_hi)
m_channel_list[offset].frequency = (m_channel_list[offset].frequency & 0x0ff) | (data << 8 & 0xf00);
else
m_channel_list[offset].frequency = (m_channel_list[offset].frequency & 0xf00) | data;
+
+ // test register bit 5 resets the internal counter
+ if (m_test & 0x20)
+ m_channel_list[offset].counter = 0;
+
+ // sample reload pending
+ m_channel_list[offset].clock = -1;
}
@@ -260,10 +274,13 @@ void k051649_device::k051649_test_w(u8 data)
}
-u8 k051649_device::k051649_test_r()
+u8 k051649_device::k051649_test_r(address_space &space)
{
- // reading the test register sets it to $ff!
+ u8 data = space.unmap();
+
+ // reading the test register triggers a write
if (!machine().side_effects_disabled())
- k051649_test_w(0xff);
- return 0xff;
+ k051649_test_w(data);
+
+ return data;
}
diff --git a/src/devices/sound/k051649.h b/src/devices/sound/k051649.h
index d9174e00dc9..10be0f28bb5 100644
--- a/src/devices/sound/k051649.h
+++ b/src/devices/sound/k051649.h
@@ -19,17 +19,18 @@ public:
k051649_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
void k051649_waveform_w(offs_t offset, u8 data);
- u8 k051649_waveform_r(offs_t offset);
+ u8 k051649_waveform_r(offs_t offset);
void k051649_volume_w(offs_t offset, u8 data);
void k051649_frequency_w(offs_t offset, u8 data);
void k051649_keyonoff_w(u8 data);
void k051649_test_w(u8 data);
- u8 k051649_test_r();
+ u8 k051649_test_r(address_space &space);
void k052539_waveform_w(offs_t offset, u8 data);
- u8 k052539_waveform_r(offs_t offset);
+ u8 k052539_waveform_r(offs_t offset);
void scc_map(address_map &map);
+
protected:
// device-level overrides
virtual void device_start() override;
@@ -41,7 +42,7 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
private:
- // Parameters for a channel
+ // parameters for a channel
struct sound_channel
{
sound_channel() :
@@ -49,28 +50,25 @@ private:
clock(0),
frequency(0),
volume(0),
+ sample(0),
key(false)
{
std::fill(std::begin(waveram), std::end(waveram), 0);
}
u8 counter; // address counter for wavetable
- u32 clock; // internal clock
+ u16 clock; // internal clock
u16 frequency; // frequency; result: (input clock / (32 * (frequency + 1)))
- int volume; // volume
+ u8 volume; // volume
+ s16 sample; // latched sample data
bool key; // keyon/off
s8 waveram[32]; // 32 byte wavetable
};
- void make_mixer_table(int voices);
-
sound_channel m_channel_list[5];
- /* global sound parameters */
sound_stream *m_stream;
-
- /* chip registers */
- u8 m_test;
+ u8 m_test; // test register
};
DECLARE_DEVICE_TYPE(K051649, k051649_device)
diff --git a/src/devices/sound/k053260.cpp b/src/devices/sound/k053260.cpp
index ccb7142d945..cdae8a4abee 100644
--- a/src/devices/sound/k053260.cpp
+++ b/src/devices/sound/k053260.cpp
@@ -49,7 +49,6 @@
converting to fractional sample position; fixed ADPCM
decoding bugs; added documentation.
-
*********************************************************/
#include "emu.h"
@@ -70,7 +69,8 @@ DEFINE_DEVICE_TYPE(K053260, k053260_device, "k053260", "K053260 KDSC")
// Pan multipliers. Set according to integer angles in degrees, amusingly.
// Exact precision hard to know, the floating point-ish output format makes
// comparisons iffy. So we used a 1.16 format.
-const int k053260_device::pan_mul[8][2] = {
+const int k053260_device::pan_mul[8][2] =
+{
{ 0, 0 }, // No sound for pan 0
{ 65536, 0 }, // 0 degrees
{ 59870, 26656 }, // 24 degrees
@@ -113,9 +113,6 @@ k053260_device::k053260_device(const machine_config &mconfig, const char *tag, d
void k053260_device::device_start()
{
- m_sh1_cb.resolve_safe();
- m_sh2_cb.resolve_safe();
-
m_stream = stream_alloc(0, 2, clock() / CLOCKS_PER_SAMPLE);
/* register with the save state system */
@@ -152,16 +149,17 @@ void k053260_device::device_reset()
attotime period = attotime::from_ticks(16, clock());
m_timer->adjust(period, 0, period);
- for (auto & elem : m_voice)
- elem.voice_reset();
+ for (auto & voice : m_voice)
+ voice.voice_reset();
}
//-------------------------------------------------
-// rom_bank_updated - the rom bank has changed
+// rom_bank_pre_change - refresh the stream if the
+// ROM banking changes
//-------------------------------------------------
-void k053260_device::rom_bank_updated()
+void k053260_device::rom_bank_pre_change()
{
m_stream->update();
}
@@ -169,11 +167,12 @@ void k053260_device::rom_bank_updated()
TIMER_CALLBACK_MEMBER(k053260_device::update_state_outputs)
{
- switch(m_timer_state) {
- case 0: m_sh1_cb(ASSERT_LINE); break;
- case 1: m_sh1_cb(CLEAR_LINE); break;
- case 2: m_sh2_cb(ASSERT_LINE); break;
- case 3: m_sh2_cb(CLEAR_LINE); break;
+ switch (m_timer_state)
+ {
+ case 0: m_sh1_cb(ASSERT_LINE); break;
+ case 1: m_sh1_cb(CLEAR_LINE); break;
+ case 2: m_sh2_cb(ASSERT_LINE); break;
+ case 3: m_sh2_cb(CLEAR_LINE); break;
}
m_timer_state = (m_timer_state+1) & 3;
}
@@ -248,15 +247,17 @@ void k053260_device::write(offs_t offset, u8 data)
// 0x04 through 0x07 seem to be unused
- case 0x28: // key on/off
+ case 0x28: // key on/off and reverse
{
u8 rising_edge = data & ~m_keyon;
for (int i = 0; i < 4; i++)
{
- if (rising_edge & (1 << i))
+ m_voice[i].set_reverse(BIT(data, i | 4));
+
+ if (BIT(rising_edge, i))
m_voice[i].key_on();
- else if (!(data & (1 << i)))
+ else if (!BIT(data, i))
m_voice[i].key_off();
}
m_keyon = data;
@@ -266,10 +267,10 @@ void k053260_device::write(offs_t offset, u8 data)
// 0x29 is a read register
case 0x2a: // loop and pcm/adpcm select
- for (auto & elem : m_voice)
+ for (int i = 0; i < 4; i++)
{
- elem.set_loop_kadpcm(data);
- data >>= 1;
+ m_voice[i].set_loop(BIT(data, i));
+ m_voice[i].set_kadpcm(BIT(data, i | 4));
}
break;
@@ -312,7 +313,7 @@ void k053260_device::sound_stream_update(sound_stream &stream, std::vector<read_
{
if (m_mode & 2)
{
- for ( int j = 0; j < outputs[0].samples(); j++ )
+ for (int j = 0; j < outputs[0].samples(); j++)
{
s32 buffer[2] = {0, 0};
@@ -354,6 +355,7 @@ void k053260_device::KDSC_Voice::voice_start(int index)
m_device.save_item(NAME(m_pan), index);
m_device.save_item(NAME(m_loop), index);
m_device.save_item(NAME(m_kadpcm), index);
+ m_device.save_item(NAME(m_reverse), index);
}
void k053260_device::KDSC_Voice::voice_reset()
@@ -369,6 +371,7 @@ void k053260_device::KDSC_Voice::voice_reset()
m_pan = 0;
m_loop = false;
m_kadpcm = false;
+ m_reverse = false;
update_pan_volume();
}
@@ -403,10 +406,19 @@ void k053260_device::KDSC_Voice::set_register(offs_t offset, u8 data)
}
}
-void k053260_device::KDSC_Voice::set_loop_kadpcm(u8 data)
+void k053260_device::KDSC_Voice::set_loop(int state)
{
- m_loop = BIT(data, 0);
- m_kadpcm = BIT(data, 4);
+ m_loop = bool(state);
+}
+
+void k053260_device::KDSC_Voice::set_kadpcm(int state)
+{
+ m_kadpcm = bool(state);
+}
+
+void k053260_device::KDSC_Voice::set_reverse(int state)
+{
+ m_reverse = bool(state);
}
void k053260_device::KDSC_Voice::set_pan(u8 data)
@@ -427,8 +439,15 @@ void k053260_device::KDSC_Voice::key_on()
m_counter = 0x1000 - CLOCKS_PER_SAMPLE; // force update on next sound_stream_update
m_output = 0;
m_playing = true;
- if (LOG) m_device.logerror("K053260: start = %06x, length = %06x, pitch = %04x, vol = %02x:%x, loop = %s, %s\n",
- m_start, m_length, m_pitch, m_volume, m_pan, m_loop ? "yes" : "no", m_kadpcm ? "KADPCM" : "PCM" );
+
+ if (LOG)
+ {
+ m_device.logerror("K053260: start = %06x, length = %06x, pitch = %04x, vol = %02x:%x, loop = %s, reverse = %s, %s\n",
+ m_start, m_length, m_pitch, m_volume, m_pan,
+ m_loop ? "yes" : "no",
+ m_reverse ? "yes" : "no",
+ m_kadpcm ? "KADPCM" : "PCM");
+ }
}
void k053260_device::KDSC_Voice::key_off()
@@ -446,7 +465,7 @@ void k053260_device::KDSC_Voice::play(s32 *outputs)
{
m_counter = m_counter - 0x1000 + m_pitch;
- u32 bytepos = ++m_position >> ( m_kadpcm ? 1 : 0 );
+ u32 bytepos = ++m_position >> (m_kadpcm ? 1 : 0);
/*
Yes, _pre_increment. Playback must start 1 byte position after the
start address written to the register, or else ADPCM sounds will
@@ -470,7 +489,7 @@ void k053260_device::KDSC_Voice::play(s32 *outputs)
}
}
- u8 romdata = m_device.read_byte(m_start + bytepos);
+ u8 romdata = m_device.read_byte(m_start + (m_reverse ? -bytepos : +bytepos));
if (m_kadpcm)
{
diff --git a/src/devices/sound/k053260.h b/src/devices/sound/k053260.h
index 401d3d2e34f..9cf86b25653 100644
--- a/src/devices/sound/k053260.h
+++ b/src/devices/sound/k053260.h
@@ -44,7 +44,7 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
// device_rom_interface overrides
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
TIMER_CALLBACK_MEMBER(update_state_outputs);
@@ -75,7 +75,9 @@ private:
inline void voice_start(int index);
inline void voice_reset();
inline void set_register(offs_t offset, u8 data);
- inline void set_loop_kadpcm(u8 data);
+ inline void set_loop(int state);
+ inline void set_kadpcm(int state);
+ inline void set_reverse(int state);
inline void set_pan(u8 data);
inline void update_pan_volume();
inline void key_on();
@@ -89,22 +91,23 @@ private:
k053260_device &m_device;
// live state
- u32 m_position = 0;
+ u32 m_position;
int m_pan_volume[2];
- u16 m_counter = 0;
- s8 m_output = 0;
- bool m_playing = false;
+ u16 m_counter;
+ s8 m_output;
+ bool m_playing;
// per voice registers
- u32 m_start = 0;
- u16 m_length = 0;
- u16 m_pitch = 0;
- u8 m_volume = 0;
+ u32 m_start;
+ u16 m_length;
+ u16 m_pitch;
+ u8 m_volume;
// bit packed registers
- u8 m_pan = 0;
- bool m_loop = false;
- bool m_kadpcm = false;
+ u8 m_pan;
+ bool m_loop;
+ bool m_kadpcm;
+ bool m_reverse;
} m_voice[4];
};
diff --git a/src/devices/sound/k054539.cpp b/src/devices/sound/k054539.cpp
index 448defed448..59f9cf2ae2e 100644
--- a/src/devices/sound/k054539.cpp
+++ b/src/devices/sound/k054539.cpp
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:Olivier Galibert
/*********************************************************
Konami 054539 (TOP) PCM Sound Chip
@@ -508,8 +508,7 @@ void k054539_device::device_start()
{
m_timer = timer_alloc(FUNC(k054539_device::call_timer_handler), this);
- // resolve / bind callbacks
- m_timer_handler.resolve_safe();
+ // resolve delegates
m_apan_cb.resolve();
for (auto & elem : gain)
@@ -557,10 +556,11 @@ void k054539_device::device_reset()
//-------------------------------------------------
-// rom_bank_updated - the rom bank has changed
+// rom_bank_pre_change - refresh the stream if the
+// ROM banking changes
//-------------------------------------------------
-void k054539_device::rom_bank_updated()
+void k054539_device::rom_bank_pre_change()
{
stream->update();
}
diff --git a/src/devices/sound/k054539.h b/src/devices/sound/k054539.h
index 406836af65a..3ee73cbf579 100644
--- a/src/devices/sound/k054539.h
+++ b/src/devices/sound/k054539.h
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:Olivier Galibert
/*********************************************************
Konami 054539 PCM Sound Chip
@@ -69,7 +69,7 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
// device_rom_interface overrides
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
TIMER_CALLBACK_MEMBER(call_timer_handler);
diff --git a/src/devices/sound/k056800.cpp b/src/devices/sound/k056800.cpp
index aae1aa10bf2..690165d838d 100644
--- a/src/devices/sound/k056800.cpp
+++ b/src/devices/sound/k056800.cpp
@@ -8,14 +8,12 @@
***********************************************************************/
#include "emu.h"
-#include "sound/k056800.h"
-
+#include "k056800.h"
DEFINE_DEVICE_TYPE(K056800, k056800_device, "k056800", "K056800 MIRAC")
-
//-------------------------------------------------
// k056800_device - constructor
//-------------------------------------------------
@@ -37,8 +35,6 @@ k056800_device::k056800_device(const machine_config &mconfig, const char *tag, d
void k056800_device::device_start()
{
- m_int_handler.resolve_safe();
-
save_item(NAME(m_int_pending));
save_item(NAME(m_int_enabled));
save_item(NAME(m_host_to_snd_regs));
diff --git a/src/devices/sound/ks0164.cpp b/src/devices/sound/ks0164.cpp
index f5c3c4d02e9..fc45a2f74cb 100644
--- a/src/devices/sound/ks0164.cpp
+++ b/src/devices/sound/ks0164.cpp
@@ -9,6 +9,43 @@
DEFINE_DEVICE_TYPE(KS0164, ks0164_device, "ks0164", "Samsung KS0164 Wavetable Synthesizer")
+// Picked up from vrender0
+const u16 ks0164_device::sample_dec[0x100]=
+{
+ 0x8000, 0x8400, 0x8800, 0x8c00, 0x9000, 0x9400, 0x9800, 0x9c00,
+ 0xa000, 0xa400, 0xa800, 0xac00, 0xb000, 0xb400, 0xb800, 0xbc00,
+ 0x4000, 0x4400, 0x4800, 0x4c00, 0x5000, 0x5400, 0x5800, 0x5c00,
+ 0x6000, 0x6400, 0x6800, 0x6c00, 0x7000, 0x7400, 0x7800, 0x7c00,
+ 0xc000, 0xc200, 0xc400, 0xc600, 0xc800, 0xca00, 0xcc00, 0xce00,
+ 0xd000, 0xd200, 0xd400, 0xd600, 0xd800, 0xda00, 0xdc00, 0xde00,
+ 0x2000, 0x2200, 0x2400, 0x2600, 0x2800, 0x2a00, 0x2c00, 0x2e00,
+ 0x3000, 0x3200, 0x3400, 0x3600, 0x3800, 0x3a00, 0x3c00, 0x3e00,
+ 0xe000, 0xe100, 0xe200, 0xe300, 0xe400, 0xe500, 0xe600, 0xe700,
+ 0xe800, 0xe900, 0xea00, 0xeb00, 0xec00, 0xed00, 0xee00, 0xef00,
+ 0x1000, 0x1100, 0x1200, 0x1300, 0x1400, 0x1500, 0x1600, 0x1700,
+ 0x1800, 0x1900, 0x1a00, 0x1b00, 0x1c00, 0x1d00, 0x1e00, 0x1f00,
+ 0xf000, 0xf080, 0xf100, 0xf180, 0xf200, 0xf280, 0xf300, 0xf380,
+ 0xf400, 0xf480, 0xf500, 0xf580, 0xf600, 0xf680, 0xf700, 0xf780,
+ 0x0800, 0x0880, 0x0900, 0x0980, 0x0a00, 0x0a80, 0x0b00, 0x0b80,
+ 0x0c00, 0x0c80, 0x0d00, 0x0d80, 0x0e00, 0x0e80, 0x0f00, 0x0f80,
+ 0xf800, 0xf840, 0xf880, 0xf8c0, 0xf900, 0xf940, 0xf980, 0xf9c0,
+ 0xfa00, 0xfa40, 0xfa80, 0xfac0, 0xfb00, 0xfb40, 0xfb80, 0xfbc0,
+ 0x0400, 0x0440, 0x0480, 0x04c0, 0x0500, 0x0540, 0x0580, 0x05c0,
+ 0x0600, 0x0640, 0x0680, 0x06c0, 0x0700, 0x0740, 0x0780, 0x07c0,
+ 0xfc00, 0xfc20, 0xfc40, 0xfc60, 0xfc80, 0xfca0, 0xfcc0, 0xfce0,
+ 0xfd00, 0xfd20, 0xfd40, 0xfd60, 0xfd80, 0xfda0, 0xfdc0, 0xfde0,
+ 0x0200, 0x0220, 0x0240, 0x0260, 0x0280, 0x02a0, 0x02c0, 0x02e0,
+ 0x0300, 0x0320, 0x0340, 0x0360, 0x0380, 0x03a0, 0x03c0, 0x03e0,
+ 0xfe00, 0xfe10, 0xfe20, 0xfe30, 0xfe40, 0xfe50, 0xfe60, 0xfe70,
+ 0xfe80, 0xfe90, 0xfea0, 0xfeb0, 0xfec0, 0xfed0, 0xfee0, 0xfef0,
+ 0x0100, 0x0110, 0x0120, 0x0130, 0x0140, 0x0150, 0x0160, 0x0170,
+ 0x0180, 0x0190, 0x01a0, 0x01b0, 0x01c0, 0x01d0, 0x01e0, 0x01f0,
+ 0x0000, 0x0008, 0x0010, 0x0018, 0x0020, 0x0028, 0x0030, 0x0038,
+ 0x0040, 0x0048, 0x0050, 0x0058, 0x0060, 0x0068, 0x0070, 0x0078,
+ 0xff80, 0xff88, 0xff90, 0xff98, 0xffa0, 0xffa8, 0xffb0, 0xffb8,
+ 0xffc0, 0xffc8, 0xffd0, 0xffd8, 0xffe0, 0xffe8, 0xfff0, 0xfff8,
+};
+
ks0164_device::ks0164_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, KS0164, tag, owner, clock),
device_sound_interface(mconfig, *this),
@@ -17,7 +54,8 @@ ks0164_device::ks0164_device(const machine_config &mconfig, const char *tag, dev
m_midi_tx(*this),
m_mem_region(*this, DEVICE_SELF),
m_cpu(*this, "cpu"),
- m_mem_config("mem", ENDIANNESS_BIG, 16, 23)
+ m_mem_config("mem", ENDIANNESS_BIG, 16, 23),
+ m_notif_rom_space()
{
}
@@ -52,12 +90,20 @@ void ks0164_device::device_start()
space().install_rom(0, rend, ((1 << 23) - 1) ^ rmask, m_mem_region->base());
}
+ m_notif_rom_space = space().add_change_notifier([this] (read_or_write mode) {
+ // HACK: If something external changes the ROM space after initial load then reset the CPU because the program code also changed (used by BMkey ROM PCBs)
+ for(int voice = 0; voice < 0x20; voice++) {
+ // Disable all voices
+ m_sregs[voice][0] &= ~1;
+ }
+
+ m_cpu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
+ });
+
m_stream = stream_alloc(0, 2, clock()/3/2/2/32);
space().cache(m_mem_cache);
m_timer = timer_alloc(FUNC(ks0164_device::irq_timer_tick), this);
- m_midi_tx.resolve_safe();
-
set_data_frame(1, 8, PARITY_NONE, STOP_BITS_1);
set_rate(clock(), 542);
@@ -175,7 +221,11 @@ void ks0164_device::mpu401_ctrl_w(u8 data)
u8 ks0164_device::mpu401_data_r()
{
- // logerror("mpu pop %02x\n", m_mpu_out);
+ if (!machine().side_effects_disabled())
+ {
+ m_mpu_status &= ~MPUS_TX_FULL;
+ // logerror("mpu pop %02x\n", m_mpu_out);
+ }
return m_mpu_out;
}
@@ -205,9 +255,12 @@ void ks0164_device::mpu401_istatus_w(u8 data)
u8 ks0164_device::mpu401_r()
{
- m_mpu_status &= ~MPUS_RX_FULL;
- m_cpu->set_input_line(11, CLEAR_LINE);
- // logerror("mpu_r %02x (%04x)\n", m_mpu_in, m_cpu->pc());
+ if (!machine().side_effects_disabled())
+ {
+ m_mpu_status &= ~MPUS_RX_FULL;
+ m_cpu->set_input_line(11, CLEAR_LINE);
+ // logerror("mpu_r %02x (%04x)\n", m_mpu_in, m_cpu->pc());
+ }
return m_mpu_in;
}
@@ -273,7 +326,7 @@ void ks0164_device::bank2_select_w(offs_t, u16 data, u16 mem_mask)
u16 ks0164_device::voice_r(offs_t offset)
{
m_stream->update();
- logerror("voice read %02x.%02x -> %04x (%04x)\n", m_voice_select & 0x1f, offset, m_sregs[m_voice_select & 0x1f][offset], m_cpu->pc());
+ // logerror("voice read %02x.%02x -> %04x (%04x)\n", m_voice_select & 0x1f, offset, m_sregs[m_voice_select & 0x1f][offset], m_cpu->pc());
return m_sregs[m_voice_select & 0x1f][offset];
}
@@ -282,18 +335,18 @@ void ks0164_device::voice_w(offs_t offset, u16 data, u16 mem_mask)
m_stream->update();
u16 old = m_sregs[m_voice_select & 0x1f][offset];
COMBINE_DATA(&m_sregs[m_voice_select & 0x1f][offset]);
- if(0)
- if(m_cpu->pc() < 0x5f94 || m_cpu->pc() > 0x5fc0)
- logerror("voice %02x.%02x = %04x @ %04x (%04x)\n", m_voice_select & 0x1f, offset, m_sregs[m_voice_select & 0x1f][offset], mem_mask, m_cpu->pc());
+ if(0 && m_sregs[m_voice_select & 0x1f][offset] != old && offset == 0)
+ logerror("voice %02x.%02x = %04x @ %04x (%04x)\n", m_voice_select & 0x1f, offset, m_sregs[m_voice_select & 0x1f][offset], mem_mask, m_cpu->pc());
if(offset == 0 && (data & 1) && !(old & 1))
- logerror("keyon %02x mode=%04x (%s %c %c %c) cur=%02x%04x.%04x loop=%02x%04x.%04x end=%02x%04x.%04x pitch=%02x.%03x 10=%02x/%02x:%02x/%02x 14=%03x/%03x:%03x/%03x 18=%04x/%04x c=%04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x\n",
+ logerror("keyon %02x mode=%04x (%s %c %c %c %c) cur=%02x%04x.%04x loop=%02x%04x.%04x end=%02x%04x.%04x pitch=%x.%03x 10=%02x/%02x:%02x/%02x 14=%03x/%03x:%03x/%03x 18=%04x/%04x c=%04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x\n",
m_voice_select,
m_sregs[m_voice_select & 0x1f][0x00],
- m_sregs[m_voice_select & 0x1f][0x00] & 0x8000 ? " 8" : "16",
- m_sregs[m_voice_select & 0x1f][0x00] & 0x0400 ? 'c' : 'l',
- m_sregs[m_voice_select & 0x1f][0x00] & 0x0008 ? '3' : '-',
+ m_sregs[m_voice_select & 0x1f][0x00] & 0x8000 ? " 8" : "16", // 8-bit/16-bit samples
+ m_sregs[m_voice_select & 0x1f][0x00] & 0x0400 ? 'c' : 'l', // compressed/linear samples
+ m_sregs[m_voice_select & 0x1f][0x00] & 0x0010 ? '4' : '-',
+ m_sregs[m_voice_select & 0x1f][0x00] & 0x0008 ? 'l' : '-', // loop
m_sregs[m_voice_select & 0x1f][0x00] & 0x0004 ? '2' : '-',
m_sregs[m_voice_select & 0x1f][0x01], // cur
@@ -308,8 +361,8 @@ void ks0164_device::voice_w(offs_t offset, u16 data, u16 mem_mask)
m_sregs[m_voice_select & 0x1f][0x0e],
m_sregs[m_voice_select & 0x1f][0x0f],
- m_sregs[m_voice_select & 0x1f][0x08] & 0x1f, // pitch
- m_sregs[m_voice_select & 0x1f][0x08] >> 5,
+ m_sregs[m_voice_select & 0x1f][0x08] & 0xf, // pitch
+ m_sregs[m_voice_select & 0x1f][0x08] >> 4,
m_sregs[m_voice_select & 0x1f][0x10] >> 9,
m_sregs[m_voice_select & 0x1f][0x12] >> 9,
@@ -388,7 +441,7 @@ u8 ks0164_device::voice_select_r()
void ks0164_device::voice_select_w(u8 data)
{
m_voice_select = data;
- logerror("voice_select = %02x (%04x)\n", m_voice_select, m_cpu->pc());
+ // logerror("voice_select = %02x (%04x)\n", m_voice_select, m_cpu->pc());
}
void ks0164_device::cpu_map(address_map &map)
@@ -415,16 +468,6 @@ void ks0164_device::cpu_map(address_map &map)
map(0xe000, 0xffff).ram();
}
-u16 ks0164_device::uncomp_8_16(u8 value)
-{
- int xp = value >> 5;
- s16 o = (0x10 | (value & 0xf)) << 10;
- o = o >> xp;
- if(value & 0x10)
- o = -o;
- return o;
-}
-
void ks0164_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
{
for(int sample = 0; sample != outputs[0].samples(); sample++) {
@@ -432,12 +475,7 @@ void ks0164_device::sound_stream_update(sound_stream &stream, std::vector<read_s
for(int voice = 0; voice < 0x20; voice++) {
u16 *regs = m_sregs[voice];
if(regs[0] & 0x0001) {
-
u64 current = (u64(regs[1]) << 32) | (u64(regs[2]) << 16) | regs[3];
-
- if(current & 0xc000000000)
- continue;
-
u32 adr = current >> 16;
s16 samp0, samp1;
switch(regs[0] & 0x8400) {
@@ -447,8 +485,8 @@ void ks0164_device::sound_stream_update(sound_stream &stream, std::vector<read_s
break;
case 0x8400: // 8 bits compressed
- samp0 = uncomp_8_16(m_mem_cache.read_byte(adr));
- samp1 = uncomp_8_16(m_mem_cache.read_byte(adr+1));
+ samp0 = sample_dec[m_mem_cache.read_byte(adr)];
+ samp1 = sample_dec[m_mem_cache.read_byte(adr+1)];
break;
default:
@@ -458,25 +496,39 @@ void ks0164_device::sound_stream_update(sound_stream &stream, std::vector<read_s
}
s16 samp = samp0 + (((samp1 - samp0) * (current & 0xffff)) >> 16);
- u32 step = 0x10000 | (regs[8] & ~0x1f);
- u32 shift = regs[8] & 0x1f;
- if(shift > 0x10)
- step >>= 0x20 - shift;
+ u32 step = 0x10000 | (regs[8] & ~0xf);
+ u32 shift = regs[8] & 0xf;
+ if(shift >= 0x8)
+ step >>= 0x10 - shift;
else if(shift)
step <<= shift;
current += step;
u64 end = (u64(regs[0xd]) << 32) | (u64(regs[0xe]) << 16) | regs[0xf];
if(current >= end) {
- // Is there a loop enabled flag?
- u64 loop = (u64(regs[9]) << 32) | (regs[0xa] << 16) | regs[0xb];
- current = current - end + loop;
+ if (regs[0] & 8) {
+ u64 loop = (u64(regs[9]) << 32) | (u64(regs[0xa]) << 16) | regs[0xb];
+ while(current >= end)
+ current = current - end + loop;
+ } else {
+ regs[0] = ~1;
+ regs[0xc] = 0;
+ regs[0x10] = regs[0x12] = regs[0x14] = regs[0x16] = 0;
+ }
}
regs[1] = current >> 32;
regs[2] = current >> 16;
regs[3] = current;
- suml += samp;
- sumr += samp;
+ suml += (s64(samp) * regs[0x12] * regs[0x16]) >> 32;
+ sumr += (s64(samp) * regs[0x10] * regs[0x14]) >> 32;
+
+ if(regs[0xc]) {
+ regs[0x10] += regs[0x11];
+ regs[0x12] += regs[0x13];
+ regs[0x14] += regs[0x15];
+ regs[0x16] += regs[0x17];
+ regs[0xc] --;
+ }
}
}
outputs[0].put_int(sample, suml, 32768 * 32);
diff --git a/src/devices/sound/ks0164.h b/src/devices/sound/ks0164.h
index 208176c6e2e..ae7680c91c9 100644
--- a/src/devices/sound/ks0164.h
+++ b/src/devices/sound/ks0164.h
@@ -46,6 +46,8 @@ private:
MPUS_RX_INT = 0x80
};
+ static const u16 sample_dec[0x100];
+
devcb_write_line m_midi_tx;
optional_memory_region m_mem_region;
@@ -72,6 +74,8 @@ private:
u8 m_irqen_76, m_irqen_77;
bool m_timer_interrupt;
+ util::notifier_subscription m_notif_rom_space;
+
void cpu_map(address_map &map);
u16 vec_r(offs_t offset, u16 mem_mask);
@@ -106,8 +110,6 @@ private:
void midi_w(u8 data);
u8 midi_status_r();
void midi_status_w(u8 data);
-
- static inline u16 uncomp_8_16(u8 value);
};
DECLARE_DEVICE_TYPE(KS0164, ks0164_device)
diff --git a/src/devices/sound/l7a1045_l6028_dsp_a.cpp b/src/devices/sound/l7a1045_l6028_dsp_a.cpp
index a9db61b5deb..aeb87e47968 100644
--- a/src/devices/sound/l7a1045_l6028_dsp_a.cpp
+++ b/src/devices/sound/l7a1045_l6028_dsp_a.cpp
@@ -1,6 +1,6 @@
// license:LGPL-2.1+
// copyright-holders:David Haywood, Angelo Salese, ElSemi
-/***************************************************************************
+/**************************************************************************************************
L7A1045 L6028 DSP-A
(QFP120 package)
@@ -77,11 +77,16 @@
TODO:
- Sample format needs to be double checked;
- - Octave Control/BPM/Pitch, right now XRally Network BGM wants 66150 Hz which is definitely too fast for Terry Bogard speech;
- - Key Off;
+ - Octave Control/BPM/Pitch, xrally Network BGM wants 66150 Hz which is definitely too fast for
+ most fatfurwa samples;
+ - Key Off for looping samples (fatfurwa should stop all samples when user insert a credit,
+ cfr. reg[0] readback);
+ - Most non-looping samples are setup to repeat twice on different channels (cfr. fatfurwa);
+ - Fix relative sample end positions (non-loop);
- ADSR (registers 2 & 4?);
+ - How DMA really works?
-***************************************************************************/
+**************************************************************************************************/
#include "emu.h"
#include "l7a1045_l6028_dsp_a.h"
@@ -120,7 +125,9 @@ void l7a1045_sound_device::device_start()
assert(!(m_rom.length() & (m_rom.length() - 1)));
// Allocate the stream
- m_stream = stream_alloc(0, 2, 66150); //clock() / 384);
+// m_stream = stream_alloc(0, 2, 66150); //clock() / 384);
+ // TODO: confirm frequency
+ m_stream = stream_alloc(0, 2, 44100);
save_item(STRUCT_MEMBER(m_voice, start));
save_item(STRUCT_MEMBER(m_voice, end));
@@ -135,6 +142,10 @@ void l7a1045_sound_device::device_start()
save_item(STRUCT_MEMBER(m_audiodat, dat));
}
+void l7a1045_sound_device::device_reset()
+{
+ m_key = 0;
+}
//-------------------------------------------------
// sound_stream_update - handle a stream update
@@ -218,8 +229,10 @@ uint16_t l7a1045_sound_device::l7a1045_sound_r(offs_t offset, uint16_t mem_mask)
//logerror("%s: read at %x (mask %04x)\n", tag(), offset, mem_mask);
- if(offset == 0)
- printf("sound_select_r?\n");
+ if (offset == 0)
+ {
+ //logerror("sound_select_r?\n");
+ }
else
return sound_data_r(offset -1);
@@ -255,7 +268,7 @@ void l7a1045_sound_device::sound_data_w(offs_t offset, uint16_t data)
l7a1045_voice *vptr = &m_voice[m_audiochannel];
//if(m_audioregister != 0 && m_audioregister != 1 && m_audioregister != 7)
- // printf("%04x %04x (%04x %04x)\n",offset,data,m_audioregister,m_audiochannel);
+ // logerror("%04x %04x (%04x %04x)\n",offset,data,m_audioregister,m_audiochannel);
m_audiodat[m_audioregister][m_audiochannel].dat[offset] = data;
@@ -281,12 +294,15 @@ void l7a1045_sound_device::sound_data_w(offs_t offset, uint16_t data)
break;
case 0x01:
// relative to start
- //printf("%04x\n",m_audiodat[m_audioregister][m_audiochannel].dat[0]);
- //printf("%04x\n",m_audiodat[m_audioregister][m_audiochannel].dat[1]);
- //printf("%04x\n",m_audiodat[m_audioregister][m_audiochannel].dat[2]);
+ //logerror("%04x\n",m_audiodat[m_audioregister][m_audiochannel].dat[0]);
+ //logerror("%04x\n",m_audiodat[m_audioregister][m_audiochannel].dat[1]);
+ //logerror("%04x\n",m_audiodat[m_audioregister][m_audiochannel].dat[2]);
if(m_audiodat[m_audioregister][m_audiochannel].dat[2] & 0x100)
{
+ // TODO: definitely wrong
+ // fatfurwa title screen sample 0x45a (0x8000?)
+ // fatfurwa coin 0x3a0 (0x2000?)
vptr->end = (m_audiodat[m_audioregister][m_audiochannel].dat[0] & 0xffff) << 2;
vptr->end += vptr->start;
vptr->mode = false;
@@ -312,7 +328,7 @@ void l7a1045_sound_device::sound_data_w(offs_t offset, uint16_t data)
vptr->r_volume = (vptr->r_volume) | (vptr->r_volume << 8);
vptr->l_volume = (m_audiodat[m_audioregister][m_audiochannel].dat[0] >> 8) & 0xff;
vptr->l_volume = (vptr->l_volume) | (vptr->l_volume << 8);
- //printf("%04x %02x %02x\n",m_audiodat[m_audioregister][m_audiochannel].dat[0],vptr->l_volume,vptr->r_volume);
+ //logerror("%04x %02x %02x\n",m_audiodat[m_audioregister][m_audiochannel].dat[0],vptr->l_volume,vptr->r_volume);
break;
}
@@ -321,7 +337,7 @@ void l7a1045_sound_device::sound_data_w(offs_t offset, uint16_t data)
uint16_t l7a1045_sound_device::sound_data_r(offs_t offset)
{
- //printf("%04x (%04x %04x)\n",offset,m_audioregister,m_audiochannel);
+ //logerror("%04x (%04x %04x)\n",offset,m_audioregister,m_audiochannel);
//machine().debug_break();
l7a1045_voice *vptr = &m_voice[m_audiochannel];
@@ -332,6 +348,9 @@ uint16_t l7a1045_sound_device::sound_data_r(offs_t offset)
uint32_t current_addr;
uint16_t res;
+ // TODO: fatfurwa reads offset == 2, ANDs with 0xf and compares against a sample buffer value if it's bigger, smaller or equal
+ // Returning 0xffff here for looping samples and they will silence out when user insert a coin ...
+
current_addr = vptr->start + vptr->pos;
if(offset == 0)
res = (current_addr & 0xf) << 12; // TODO: frac
@@ -344,6 +363,9 @@ uint16_t l7a1045_sound_device::sound_data_r(offs_t offset)
}
}
+ // TODO: at least regs [3] and [5], relative position read-back?
+ // TODO: reg [6]
+
return 0;
}
@@ -356,11 +378,11 @@ void l7a1045_sound_device::sound_status_w(uint16_t data)
#if 0
if(vptr->start != 0)
{
- printf("%08x START\n",vptr->start);
- printf("%08x END\n",vptr->end);
+ logerror("%08x START\n",vptr->start);
+ logerror("%08x END\n",vptr->end);
- for(int i=0;i<0x10;i++)
- printf("%02x (%02x) = %04x%04x%04x\n",m_audiochannel,i,m_audiodat[i][m_audiochannel].dat[2],m_audiodat[i][m_audiochannel].dat[1],m_audiodat[i][m_audiochannel].dat[0]);
+ for(int i=0;i<0x10;i++)
+ logerror("%02x (%02x) = %04x%04x%04x\n",m_audiochannel,i,m_audiodat[i][m_audiochannel].dat[2],m_audiodat[i][m_audiochannel].dat[1],m_audiodat[i][m_audiochannel].dat[0]);
}
#endif
@@ -370,7 +392,8 @@ void l7a1045_sound_device::sound_status_w(uint16_t data)
}
}
-WRITE_LINE_MEMBER(l7a1045_sound_device::dma_hreq_cb)
+// TODO: stub functions not really used
+void l7a1045_sound_device::dma_hreq_cb(int state)
{
// m_maincpu->hack_w(1);
}
diff --git a/src/devices/sound/l7a1045_l6028_dsp_a.h b/src/devices/sound/l7a1045_l6028_dsp_a.h
index 4fff2caa465..58877c0836c 100644
--- a/src/devices/sound/l7a1045_l6028_dsp_a.h
+++ b/src/devices/sound/l7a1045_l6028_dsp_a.h
@@ -26,11 +26,12 @@ public:
void dma_w_cb(offs_t offset, uint8_t data);
uint16_t dma_r16_cb() { m_voice[0].pos++; return 0; }
void dma_w16_cb(uint16_t data) { m_voice[0].pos++; }
- DECLARE_WRITE_LINE_MEMBER(dma_hreq_cb);
+ void dma_hreq_cb(int state);
protected:
// device-level overrides
virtual void device_start() override;
+ virtual void device_reset() override;
// sound stream update overrides
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
diff --git a/src/devices/sound/lc7535.cpp b/src/devices/sound/lc7535.cpp
index 0f05bb14456..21a921668e3 100644
--- a/src/devices/sound/lc7535.cpp
+++ b/src/devices/sound/lc7535.cpp
@@ -40,7 +40,7 @@ DEFINE_DEVICE_TYPE(LC7535, lc7535_device, "lc7535", "Sanyo LC7535")
lc7535_device::lc7535_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
device_t(mconfig, LC7535, tag, owner, clock),
- m_select_cb(*this),
+ m_select_cb(*this, 1),
m_volume_cb(*this),
m_addr(0), m_data(0),
m_count(0),
@@ -54,8 +54,7 @@ lc7535_device::lc7535_device(const machine_config &mconfig, const char *tag, dev
void lc7535_device::device_start()
{
- // resolve callbacks
- m_select_cb.resolve();
+ // resolve delegates
m_volume_cb.resolve();
// register for save states
@@ -89,17 +88,17 @@ float lc7535_device::normalize(int attenuation)
// INTERFACE
//**************************************************************************
-WRITE_LINE_MEMBER( lc7535_device::ce_w )
+void lc7535_device::ce_w(int state)
{
m_ce = state;
}
-WRITE_LINE_MEMBER( lc7535_device::di_w )
+void lc7535_device::di_w(int state)
{
m_di = state;
}
-WRITE_LINE_MEMBER( lc7535_device::clk_w )
+void lc7535_device::clk_w(int state)
{
if (m_clk == 0 && state == 1)
{
diff --git a/src/devices/sound/lc7535.h b/src/devices/sound/lc7535.h
index 4fcf649abd9..cf496188651 100644
--- a/src/devices/sound/lc7535.h
+++ b/src/devices/sound/lc7535.h
@@ -45,9 +45,9 @@ public:
template <typename... T> void set_volume_callback(T &&... args) { m_volume_cb.set(std::forward<T>(args)...); }
// serial interface
- DECLARE_WRITE_LINE_MEMBER( ce_w );
- DECLARE_WRITE_LINE_MEMBER( di_w );
- DECLARE_WRITE_LINE_MEMBER( clk_w );
+ void ce_w(int state);
+ void di_w(int state);
+ void clk_w(int state);
float normalize(int attenuation);
diff --git a/src/devices/sound/lc78836m.cpp b/src/devices/sound/lc78836m.cpp
new file mode 100644
index 00000000000..7b611e59cf7
--- /dev/null
+++ b/src/devices/sound/lc78836m.cpp
@@ -0,0 +1,208 @@
+// license:BSD-3-Clause
+// copyright-holders:windyfairy
+/***************************************************************************
+
+ Sanyo LC78836M Digital Audio 16-bit D/A Converter
+ with On-Chip Digital Filters
+ ____ ____
+ REFH 1 | \_/ | 24 AVDD
+ VrefH 2 | | 23 CH1OUT
+ MUTE 3 | | 22 AGND
+ D/N 4 | | 21 CH2OUT
+ BCLK 5 | | 20 REFL
+ DATA 6 | LC78836M | 19 VrefL
+ LRCK 7 | | 18 CKSL2
+ DVDD 8 | | 17 CKSL1
+ CKOUT 9 | | 16 FS2
+ XOUT 10 | | 15 FS1
+ XIN 11 | | 14 EMP
+ DGND 12 |___________| 13 INITB
+
+
+ TOOD:
+ - INITB, DN aren't implemented
+ - Filters aren't implemented
+
+***************************************************************************/
+
+#include "emu.h"
+#include "lc78836m.h"
+
+// #define VERBOSE (LOG_GENERAL)
+
+#include "logmacro.h"
+
+
+DEFINE_DEVICE_TYPE(LC78836M, lc78836m_device, "lc78836m", "Sanyo LC78836M Digital Audio 16-bit D/A Converter")
+
+
+lc78836m_device::lc78836m_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, LC78836M, tag, owner, clock)
+ , device_sound_interface(mconfig, *this)
+ , m_stream(nullptr)
+ , m_cksl1(0), m_cksl2(0)
+ , m_fs1(0), m_fs2(0)
+ , m_emp(0)
+{
+}
+
+void lc78836m_device::device_start()
+{
+ save_item(NAME(m_cksl1));
+ save_item(NAME(m_cksl2));
+ save_item(NAME(m_fs1));
+ save_item(NAME(m_fs2));
+ save_item(NAME(m_emp));
+
+ save_item(NAME(m_mute));
+ save_item(NAME(m_lrck));
+ save_item(NAME(m_data));
+ save_item(NAME(m_bclk));
+ save_item(NAME(m_sample));
+ save_item(NAME(m_sample_bit));
+ save_item(NAME(m_clock_fs));
+
+ save_item(NAME(m_sample_ch1));
+ save_item(NAME(m_sample_ch2));
+ save_item(NAME(m_att));
+
+ m_stream = stream_alloc(2, 2, m_clock);
+}
+
+void lc78836m_device::device_reset()
+{
+ m_mute = 0;
+ m_lrck = 0;
+ m_data = 0;
+ m_bclk = 0;
+ m_sample = 0;
+ m_sample_bit = 0;
+ m_att = 1024;
+ m_sample_ch1 = m_sample_ch2 = 0;
+
+ update_clock();
+}
+
+void lc78836m_device::device_clock_changed()
+{
+ update_clock();
+}
+
+void lc78836m_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
+{
+ outputs[0].fill(0);
+ outputs[0].put(0, m_sample_ch1 * m_att / 1024.0);
+ m_sample_ch1 = 0;
+
+ outputs[1].fill(0);
+ outputs[1].put(0, m_sample_ch2 * m_att / 1024.0);
+ m_sample_ch2 = 0;
+
+ if (m_mute && m_att > 0)
+ m_att--;
+ else if (!m_mute && m_att < 1024)
+ m_att++;
+
+}
+
+void lc78836m_device::mute_w(int state)
+{
+ // Soft mute
+ m_mute = state;
+}
+
+void lc78836m_device::lrck_w(int state)
+{
+ // CH1 when high, CH2 when low
+ m_lrck = state;
+}
+
+void lc78836m_device::data_w(int state)
+{
+ m_data = state;
+}
+
+void lc78836m_device::bclk_w(int state)
+{
+ if (!m_bclk && state) {
+ m_sample |= m_data << m_sample_bit;
+ m_sample_bit++;
+
+ if (m_sample_bit >= 16) {
+ stream_buffer::sample_t sample = m_sample / double(std::numeric_limits<int16_t>::max());
+
+ if (m_lrck)
+ m_sample_ch1 = sample;
+ else
+ m_sample_ch2 = sample;
+
+ m_sample = 0;
+ m_sample_bit = 0;
+
+ m_stream->update();
+ }
+ }
+
+ m_bclk = state;
+}
+
+void lc78836m_device::initb_w(int state)
+{
+ // Initialization signal input
+ // LSI is initialized when set low
+ m_initb = state;
+}
+
+void lc78836m_device::cksl1_w(int state)
+{
+ // System clock selection bit 1
+ if (state != m_cksl1) {
+ m_cksl1 = state;
+ update_clock();
+ }
+}
+
+void lc78836m_device::cksl2_w(int state)
+{
+ // System clock selection bit 2
+ if (state != m_cksl2) {
+ m_cksl2 = state;
+ update_clock();
+ }
+}
+
+void lc78836m_device::fs1_w(int state)
+{
+ // De-emphasis filter mode selection bit 1
+ m_fs1 = state;
+}
+
+void lc78836m_device::fs2_w(int state)
+{
+ // De-emphasis filter mode selection bit 2
+ m_fs2 = state;
+}
+
+void lc78836m_device::emp_w(int state)
+{
+ // De-emphasis filter on/off
+ m_emp = state;
+}
+
+void lc78836m_device::update_clock()
+{
+ if (m_cksl1 == 0 && m_cksl2 == 0)
+ m_clock_fs = 384;
+ else if (m_cksl1 == 0 && m_cksl2 == 1)
+ m_clock_fs = 392;
+ else if (m_cksl1 == 1 && m_cksl2 == 0)
+ m_clock_fs = 448;
+ else if (m_cksl1 == 1 && m_cksl2 == 1)
+ m_clock_fs = 512;
+
+ const uint32_t new_sample_rate = m_clock / m_clock_fs;
+ if (m_stream != nullptr && new_sample_rate != m_stream->sample_rate()) {
+ LOG("sample rate changed to %d\n", new_sample_rate);
+ m_stream->set_sample_rate(new_sample_rate);
+ }
+}
diff --git a/src/devices/sound/lc78836m.h b/src/devices/sound/lc78836m.h
new file mode 100644
index 00000000000..3a95e79b656
--- /dev/null
+++ b/src/devices/sound/lc78836m.h
@@ -0,0 +1,66 @@
+// license:BSD-3-Clause
+// copyright-holders:windyfairy
+/***************************************************************************
+
+ Sanyo LC78836M Digital Audio 16-bit D/A Converter
+ with On-Chip Digital Filters
+
+***************************************************************************/
+
+#ifndef MAME_SOUND_LC78836M_H
+#define MAME_SOUND_LC78836M_H
+
+#pragma once
+
+class lc78836m_device : public device_t, public device_sound_interface
+{
+public:
+ lc78836m_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ void mute_w(int state);
+
+ void lrck_w(int state);
+ void data_w(int state);
+ void bclk_w(int state);
+
+ void cksl1_w(int state);
+ void cksl2_w(int state);
+
+ void fs1_w(int state);
+ void fs2_w(int state);
+
+ void emp_w(int state);
+
+ void initb_w(int state);
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_clock_changed() override;
+
+ virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
+
+private:
+ void update_clock();
+
+ sound_stream *m_stream;
+
+ uint8_t m_mute;
+ uint8_t m_cksl1, m_cksl2;
+ uint16_t m_clock_fs;
+ uint8_t m_fs1, m_fs2;
+ uint8_t m_emp;
+ uint8_t m_initb;
+
+ uint8_t m_bclk, m_lrck, m_data;
+ uint8_t m_sample_bit;
+ int16_t m_sample;
+
+ stream_buffer::sample_t m_sample_ch1, m_sample_ch2;
+ double m_att;
+};
+
+
+DECLARE_DEVICE_TYPE(LC78836M, lc78836m_device)
+
+#endif // MAME_SOUND_LC78836M_H
diff --git a/src/devices/sound/lc82310.cpp b/src/devices/sound/lc82310.cpp
new file mode 100644
index 00000000000..3404a5c9629
--- /dev/null
+++ b/src/devices/sound/lc82310.cpp
@@ -0,0 +1,301 @@
+// license:BSD-3-Clause
+// copyright-holders:windyfairy
+/**********************************************************************
+
+ Sanyo LC82310 MP3 decoder
+
+**********************************************************************/
+
+#include "emu.h"
+#include "lc82310.h"
+#include "mp3_audio.h"
+
+DEFINE_DEVICE_TYPE(LC82310, lc82310_device, "lc82310", "Sanyo LC82310 MP3 Decoder")
+
+lc82310_device::lc82310_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, LC82310, tag, owner, clock)
+ , device_sound_interface(mconfig, *this)
+{
+}
+
+void lc82310_device::device_start()
+{
+ stream = stream_alloc(0, 2, 44100);
+ mp3dec = std::make_unique<mp3_audio>(reinterpret_cast<const uint8_t *>(&mp3data[0]));
+
+ save_item(NAME(mp3data));
+ save_item(NAME(samples));
+ save_item(NAME(m_mp3data_count));
+ save_item(NAME(m_sample_count));
+ save_item(NAME(m_samples_idx));
+ save_item(NAME(m_frame_channels));
+ save_item(NAME(m_output_gain));
+
+ save_item(NAME(m_csctl));
+ save_item(NAME(m_ckctl));
+ save_item(NAME(m_dictl));
+ save_item(NAME(m_doctl));
+ save_item(NAME(m_ctl_state));
+ save_item(NAME(m_ctl_cmd));
+ save_item(NAME(m_ctl_bits));
+ save_item(NAME(m_ctl_byte));
+ save_item(NAME(m_ctl_out_byte));
+
+ mp3dec->register_save(*this);
+}
+
+void lc82310_device::device_reset()
+{
+ std::fill(std::begin(m_output_gain), std::end(m_output_gain), 0);
+
+ m_csctl = 0;
+ m_ckctl = 0;
+ m_dictl = 0;
+ m_doctl = 0;
+ m_ctl_bits = 0;
+ m_ctl_byte = 0;
+ m_ctl_out_byte = 0;
+ m_ctl_state = ACCEPTING_CMD;
+
+ reset_playback();
+}
+
+void lc82310_device::reset_playback()
+{
+ std::fill(mp3data.begin(), mp3data.end(), 0);
+ std::fill(samples.begin(), samples.end(), 0);
+
+ mp3dec->clear();
+ m_mp3data_count = 0;
+ m_sample_count = 0;
+ m_samples_idx = 0;
+ m_frame_channels = 2;
+}
+
+void lc82310_device::zcsctl_w(int state)
+{
+ m_csctl = state;
+}
+
+void lc82310_device::ckctl_w(int state)
+{
+ if (m_csctl == 0 && m_ckctl == 0 && state == 1)
+ {
+ m_ctl_byte |= m_dictl << m_ctl_bits;
+ m_ctl_bits++;
+
+ if (m_ctl_bits > 7)
+ {
+ if (m_ctl_state == ACCEPTING_CMD)
+ {
+ // Expected to be able to read the return value while sending the second byte
+ // Everything in the 0x80 range of commands seems to respond with a value
+ // 0x80, 0x81, 0x82 return separate 8-bit values
+ // 0x83 returns an 8-bit error status, bits 0 and 1 are used to signal errors, any non-0 bit is considered an error
+ // 0x84 and 0x85 are used together to form a 16-bit value
+ m_ctl_out_byte = 0;
+
+ m_ctl_cmd = m_ctl_byte;
+ m_ctl_state = ACCEPTING_PARAM;
+ }
+ else if (m_ctl_state == ACCEPTING_PARAM)
+ {
+ handle_command(m_ctl_cmd, m_ctl_byte);
+ m_ctl_state = ACCEPTING_CMD;
+ }
+
+ m_ctl_byte = 0;
+ m_ctl_bits = 0;
+ }
+
+ m_doctl = m_ctl_out_byte & 1;
+ m_ctl_out_byte >>= 1;
+ }
+
+ m_ckctl = state;
+}
+
+void lc82310_device::dictl_w(int state)
+{
+ m_dictl = state;
+}
+
+int lc82310_device::doctl_r()
+{
+ return m_doctl;
+}
+
+int lc82310_device::demand_r()
+{
+ return m_mp3data_count < mp3data.size();
+}
+
+void lc82310_device::dimpg_w(uint8_t data)
+{
+ if (m_mp3data_count >= mp3data.size())
+ {
+ // Drop a byte if the buffer is full and it's still trying to send data
+ std::copy(mp3data.begin() + 1, mp3data.end(), mp3data.begin());
+ m_mp3data_count--;
+ }
+
+ mp3data[m_mp3data_count++] = data;
+}
+
+void lc82310_device::handle_command(uint8_t cmd, uint8_t param)
+{
+ if (cmd == CMD_UNK13_VOL || cmd == CMD_UNK15_VOL)
+ {
+ // These are calculated based on the configurable values in-game vs what is sent to the MP3 decoder
+ constexpr float gain_table[] = {
+ 1.0, // 0
+ 30.0 / 31.0, // 1
+ 29.0 / 31.0, // 2
+ 28.0 / 31.0, // 3
+ 27.0 / 31.0, // 4
+ 26.0 / 31.0, // 5
+ 25.0 / 31.0, // 6
+ 24.0 / 31.0, // 7
+ 23.0 / 31.0, // 8
+ 22.0 / 31.0, // 9
+ 21.0 / 31.0, // 10
+ 20.0 / 31.0, // 11
+ 19.0 / 31.0, // 12
+ 18.0 / 31.0, // 13
+ 17.0 / 31.0, // 14
+ 16.0 / 31.0, // 15
+ 15.0 / 31.0, // 16
+ 14.0 / 31.0, // 17
+ 13.0 / 31.0, // 18
+ 12.0 / 31.0, // 19
+ 11.0 / 31.0, // 20
+ 10.0 / 31.0, // 21
+ 9.0 / 31.0, // 22
+ 8.5 / 31.0, // 23
+ 8.0 / 31.0, // 24
+ 7.0 / 31.0, // 25
+ 6.0 / 31.0, // 25
+ 5.5 / 31.0, // 27
+ 5.0 / 31.0, // 28
+ 4.5 / 31.0, // 29
+ 4.0 / 31.0, // 30
+ 3.5 / 31.0, // 31
+ 3.0 / 31.0, // 32
+ 2.75 / 31.0, // 33
+ 2.5 / 31.0, // 34
+ 2.25 / 31.0, // 35
+ 2.0 / 31.0, // 36
+ (1.0 + (1.0 / 6) * 5) / 31.0, // 37
+ (1.0 + (1.0 / 6) * 4) / 31.0, // 38
+ (1.0 + (1.0 / 6) * 3) / 31.0, // 39
+ (1.0 + (1.0 / 6) * 2) / 31.0, // 40
+ (1.0 + (1.0 / 6) * 1) / 31.0, // 41
+ 1.0 / 31.0, // 42
+ ((1.0 / 34) * 33) / 31.0, // 43
+ ((1.0 / 34) * 32) / 31.0, // 44
+ ((1.0 / 34) * 31) / 31.0, // 45
+ ((1.0 / 34) * 30) / 31.0, // 46
+ ((1.0 / 34) * 29) / 31.0, // 47
+ ((1.0 / 34) * 28) / 31.0, // 48
+ ((1.0 / 34) * 27) / 31.0, // 49
+ ((1.0 / 34) * 26) / 31.0, // 50
+ ((1.0 / 34) * 25) / 31.0, // 51
+ ((1.0 / 34) * 24) / 31.0, // 52
+ ((1.0 / 34) * 23) / 31.0, // 53
+ ((1.0 / 34) * 22) / 31.0, // 54
+ ((1.0 / 34) * 21) / 31.0, // 55
+ ((1.0 / 34) * 20) / 31.0, // 56
+ ((1.0 / 34) * 19) / 31.0, // 57
+ ((1.0 / 34) * 18) / 31.0, // 58
+ ((1.0 / 34) * 17) / 31.0, // 59
+ ((1.0 / 34) * 16) / 31.0, // 60
+ ((1.0 / 34) * 15) / 31.0, // 61
+ ((1.0 / 34) * 14) / 31.0, // 62
+ ((1.0 / 34) * 13) / 31.0, // 63
+ ((1.0 / 34) * 12) / 31.0, // 64
+ ((1.0 / 34) * 11) / 31.0, // 65
+ ((1.0 / 34) * 10) / 31.0, // 66
+ ((1.0 / 34) * 9) / 31.0, // 67
+ ((1.0 / 34) * 8) / 31.0, // 68
+ ((1.0 / 34) * 7) / 31.0, // 69
+ ((1.0 / 34) * 6) / 31.0, // 70
+ ((1.0 / 34) * 5) / 31.0, // 71
+ ((1.0 / 34) * 4) / 31.0, // 72
+ ((1.0 / 34) * 3) / 31.0, // 73
+ ((1.0 / 34) * 2) / 31.0, // 74
+ ((1.0 / 34) * 1) / 31.0, // 75
+ 0.0, // 76
+ };
+
+ int speaker_idx = cmd == CMD_UNK15_VOL ? 1 : 0; // guessed, both are set at the same time in current use cases
+ m_output_gain[speaker_idx] = gain_table[std::min<uint8_t>(param, 0x4c)];
+
+ set_output_gain(speaker_idx, m_output_gain[speaker_idx]);
+ }
+}
+
+void lc82310_device::fill_buffer()
+{
+ int pos = 0, frame_sample_rate = 0;
+ bool decoded_frame = mp3dec->decode_buffer(pos, m_mp3data_count, &samples[0], m_sample_count, frame_sample_rate, m_frame_channels);
+ m_samples_idx = 0;
+
+ if (!decoded_frame || m_sample_count <= 0)
+ {
+ // Frame decode failed
+ if (m_mp3data_count >= mp3data.size())
+ {
+ std::copy(mp3data.begin() + 1, mp3data.end(), mp3data.begin());
+ m_mp3data_count--;
+ }
+
+ return;
+ }
+
+ std::copy(mp3data.begin() + pos, mp3data.end(), mp3data.begin());
+ m_mp3data_count -= pos;
+
+ stream->set_sample_rate(frame_sample_rate);
+}
+
+void lc82310_device::append_buffer(std::vector<write_stream_view> &outputs, int &pos, int scount)
+{
+ int s1 = std::min(scount - pos, m_sample_count);
+ int words_per_sample = std::min(m_frame_channels, 2);
+
+ for (int i = 0; i < s1; i++)
+ {
+ outputs[0].put_int(pos, samples[m_samples_idx * words_per_sample], 32768);
+ outputs[1].put_int(pos, samples[m_samples_idx * words_per_sample + (words_per_sample >> 1)], 32768);
+
+ m_samples_idx++;
+ pos++;
+
+ if (m_samples_idx >= m_sample_count)
+ {
+ m_sample_count = 0;
+ return;
+ }
+ }
+}
+
+void lc82310_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
+{
+ int csamples = outputs[0].samples();
+ int pos = 0;
+
+ while (pos < csamples)
+ {
+ if (m_sample_count == 0)
+ fill_buffer();
+
+ if (m_sample_count <= 0)
+ {
+ outputs[0].fill(0, pos);
+ outputs[1].fill(0, pos);
+ return;
+ }
+
+ append_buffer(outputs, pos, csamples);
+ }
+}
diff --git a/src/devices/sound/lc82310.h b/src/devices/sound/lc82310.h
new file mode 100644
index 00000000000..559972b564e
--- /dev/null
+++ b/src/devices/sound/lc82310.h
@@ -0,0 +1,92 @@
+// license:BSD-3-Clause
+// copyright-holders:windyfairy
+/**********************************************************************
+
+ Sanyo LC82310 MP3 decoder
+
+**********************************************************************/
+
+#ifndef MAME_SOUND_LC82310_H
+#define MAME_SOUND_LC82310_H
+
+#pragma once
+
+#include "mp3_audio.h"
+
+class lc82310_device : public device_t,
+ public device_sound_interface
+{
+public:
+ lc82310_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
+
+ void zcsctl_w(int state);
+ void ckctl_w(int state);
+ void dictl_w(int state);
+ int doctl_r();
+ int demand_r();
+
+ void dimpg_w(uint8_t data);
+
+ void reset_playback();
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
+
+private:
+ enum : uint8_t
+ {
+ ACCEPTING_CMD,
+ ACCEPTING_PARAM,
+ };
+
+ enum : uint8_t
+ {
+ CMD_UNK10 = 0x10,
+ CMD_UNK11 = 0x11,
+ CMD_UNK12 = 0x12, // Set to 0 when writing data and 1 when not writing data
+ CMD_UNK13_VOL = 0x13,
+ CMD_UNK15_VOL = 0x15,
+ CMD_UNK17 = 0x17,
+ CMD_UNK18 = 0x18,
+ CMD_SET_CONFIGURATION = 0x22, // has PLLOFF and SLEEP bits
+ CMD_UNK80 = 0x80,
+ CMD_UNK81 = 0x81,
+ CMD_UNK82 = 0x82,
+ CMD_GET_ERROR_STATUS = 0x83,
+ CMD_UNK84 = 0x84,
+ CMD_UNK85 = 0x85,
+ };
+
+ void handle_command(uint8_t cmd, uint8_t param);
+
+ void fill_buffer();
+ void append_buffer(std::vector<write_stream_view> &outputs, int &pos, int scount);
+
+ sound_stream *stream;
+ std::unique_ptr<mp3_audio> mp3dec;
+
+ std::array<uint8_t, 0x1000> mp3data;
+ std::array<short, 1152*2> samples;
+
+ uint32_t m_mp3data_count;
+ int32_t m_sample_count, m_samples_idx;
+ int32_t m_frame_channels;
+ float m_output_gain[2];
+
+ uint8_t m_csctl;
+ uint8_t m_ckctl;
+ uint8_t m_dictl;
+ uint8_t m_doctl;
+ uint8_t m_ctl_state;
+ uint8_t m_ctl_cmd;
+ uint8_t m_ctl_bits;
+ uint8_t m_ctl_byte;
+ uint8_t m_ctl_out_byte;
+};
+
+DECLARE_DEVICE_TYPE(LC82310, lc82310_device)
+
+#endif // MAME_SOUND_LC82310_H
diff --git a/src/devices/sound/lmc1992.cpp b/src/devices/sound/lmc1992.cpp
index c3f2d6d3bf4..feaa0810226 100644
--- a/src/devices/sound/lmc1992.cpp
+++ b/src/devices/sound/lmc1992.cpp
@@ -185,7 +185,7 @@ void lmc1992_device::sound_stream_update(sound_stream &stream, std::vector<read_
// clock_w -
//-------------------------------------------------
-WRITE_LINE_MEMBER( lmc1992_device::clock_w )
+void lmc1992_device::clock_w(int state)
{
if ((m_enable == 0) && ((m_clk == 0) && (state == 1)))
{
@@ -206,7 +206,7 @@ WRITE_LINE_MEMBER( lmc1992_device::clock_w )
// data_w -
//-------------------------------------------------
-WRITE_LINE_MEMBER( lmc1992_device::data_w )
+void lmc1992_device::data_w(int state)
{
m_data = state;
}
@@ -216,7 +216,7 @@ WRITE_LINE_MEMBER( lmc1992_device::data_w )
// enable_w -
//-------------------------------------------------
-WRITE_LINE_MEMBER( lmc1992_device::enable_w )
+void lmc1992_device::enable_w(int state)
{
if ((m_enable == 0) && (state == 1))
{
diff --git a/src/devices/sound/lmc1992.h b/src/devices/sound/lmc1992.h
index beb893d9983..d728757299c 100644
--- a/src/devices/sound/lmc1992.h
+++ b/src/devices/sound/lmc1992.h
@@ -62,9 +62,9 @@ public:
// construction/destruction
lmc1992_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
- DECLARE_WRITE_LINE_MEMBER( clock_w );
- DECLARE_WRITE_LINE_MEMBER( data_w );
- DECLARE_WRITE_LINE_MEMBER( enable_w );
+ void clock_w(int state);
+ void data_w(int state);
+ void enable_w(int state);
protected:
// device-level overrides
diff --git a/src/devices/sound/lynx.cpp b/src/devices/sound/lynx.cpp
index d546e4ce00c..1a1f20d0bda 100644
--- a/src/devices/sound/lynx.cpp
+++ b/src/devices/sound/lynx.cpp
@@ -177,7 +177,7 @@ void lynx_sound_device::init()
void lynx_sound_device::device_start()
{
m_mixer_channel = stream_alloc(0, 1, clock() / 16);
- m_timer_delegate.resolve();
+ m_timer_delegate.resolve_safe();
init();
register_save();
}
@@ -186,7 +186,7 @@ void lynx_sound_device::device_start()
void lynx2_sound_device::device_start()
{
m_mixer_channel = stream_alloc(0, 2, clock() / 16);
- m_timer_delegate.resolve();
+ m_timer_delegate.resolve_safe();
init();
register_save();
}
@@ -279,8 +279,7 @@ void lynx_sound_device::shift(int chan_nr)
case 1: count_down(2); break;
case 2: count_down(3); break;
case 3:
- if (!m_timer_delegate.isnull())
- m_timer_delegate();
+ m_timer_delegate();
break;
}
}
diff --git a/src/devices/sound/mas3507d.cpp b/src/devices/sound/mas3507d.cpp
index eb156ec63b7..25f9d2ab1af 100644
--- a/src/devices/sound/mas3507d.cpp
+++ b/src/devices/sound/mas3507d.cpp
@@ -13,20 +13,13 @@
#include "emu.h"
#include "mas3507d.h"
+#include "mp3_audio.h"
-#define MINIMP3_ONLY_MP3
-#define MINIMP3_NO_STDIO
-#define MINIMP3_IMPLEMENTATION
-#define MAX_FRAME_SYNC_MATCHES 3
-#include "minimp3/minimp3.h"
-#include "minimp3/minimp3_ex.h"
-
-#define LOG_GENERAL (1 << 0)
-#define LOG_READ (1 << 1)
-#define LOG_WRITE (1 << 2)
-#define LOG_REGISTER (1 << 3)
-#define LOG_CONFIG (1 << 4)
-#define LOG_OTHER (1 << 5)
+#define LOG_READ (1U << 1)
+#define LOG_WRITE (1U << 2)
+#define LOG_REGISTER (1U << 3)
+#define LOG_CONFIG (1U << 4)
+#define LOG_OTHER (1U << 5)
// #define VERBOSE (LOG_GENERAL | LOG_READ | LOG_WRITE | LOG_REGISTER | LOG_CONFIG | LOG_OTHER)
// #define LOG_OUTPUT_STREAM std::cout
@@ -59,9 +52,7 @@ mas3507d_device::mas3507d_device(const machine_config &mconfig, const char *tag,
void mas3507d_device::device_start()
{
stream = stream_alloc(0, 2, 44100);
-
- cb_mpeg_frame_sync.resolve();
- cb_demand.resolve();
+ mp3dec = std::make_unique<mp3_audio>(reinterpret_cast<const uint8_t *>(&mp3data[0]));
save_item(NAME(mp3data));
save_item(NAME(samples));
@@ -78,7 +69,6 @@ void mas3507d_device::device_start()
save_item(NAME(mp3data_count));
save_item(NAME(decoded_frame_count));
- save_item(NAME(decoded_samples));
save_item(NAME(sample_count));
save_item(NAME(samples_idx));
save_item(NAME(is_muted));
@@ -90,23 +80,10 @@ void mas3507d_device::device_start()
save_item(NAME(i2c_io_count));
save_item(NAME(i2c_io_val));
save_item(NAME(i2c_sdao_data));
- save_item(NAME(playback_status));
- save_item(NAME(mp3_is_buffered));
-
- // This should be removed in the future if/when native MP3 decoding is implemented in MAME
- save_item(NAME(mp3_dec.mdct_overlap));
- save_item(NAME(mp3_dec.qmf_state));
- save_item(NAME(mp3_dec.reserv));
- save_item(NAME(mp3_dec.free_format_bytes));
- save_item(NAME(mp3_dec.header));
- save_item(NAME(mp3_dec.reserv_buf));
-
- save_item(NAME(mp3_info.frame_bytes));
- save_item(NAME(mp3_info.frame_offset));
- save_item(NAME(mp3_info.channels));
- save_item(NAME(mp3_info.hz));
- save_item(NAME(mp3_info.layer));
- save_item(NAME(mp3_info.bitrate_kbps));
+
+ save_item(NAME(frame_channels));
+
+ mp3dec->register_save(*this);
}
void mas3507d_device::device_reset()
@@ -117,9 +94,17 @@ void mas3507d_device::device_reset()
i2c_bus_address = UNKNOWN;
i2c_bus_curbit = -1;
i2c_bus_curval = 0;
+ i2c_bytecount = 0;
+ i2c_io_bank = 0;
+ i2c_io_adr = 0;
+ i2c_io_count = 0;
+ i2c_io_val = 0;
+ i2c_sdao_data = 0;
is_muted = false;
- gain_ll = gain_rr = 0;
+ gain_ll = gain_rr = 1.0;
+
+ frame_channels = 2;
stream->set_sample_rate(44100);
@@ -364,7 +349,8 @@ int mas3507d_device::gain_to_db(double val) {
return round(20 * log10((0x100000 - val) / 0x80000));
}
-float mas3507d_device::gain_to_percentage(int val) {
+float mas3507d_device::gain_to_percentage(int val)
+{
if(val == 0)
return 0; // Special case for muting it seems
@@ -382,10 +368,6 @@ void mas3507d_device::mem_write(int bank, uint32_t adr, uint32_t val)
case 0x107f8:
gain_ll = gain_to_percentage(val);
LOGCONFIG("MAS3507D: left->left gain = %05x (%d dB, %f%%)\n", val, gain_to_db(val), gain_ll);
-
- if(!is_muted) {
- set_output_gain(0, gain_ll);
- }
break;
case 0x107f9:
LOGCONFIG("MAS3507D: left->right gain = %05x (%d dB, %f%%)\n", val, gain_to_db(val), gain_to_percentage(val));
@@ -396,10 +378,6 @@ void mas3507d_device::mem_write(int bank, uint32_t adr, uint32_t val)
case 0x107fb:
gain_rr = gain_to_percentage(val);
LOGCONFIG("MAS3507D: right->right gain = %05x (%d dB, %f%%)\n", val, gain_to_db(val), gain_rr);
-
- if(!is_muted) {
- set_output_gain(1, gain_rr);
- }
break;
default: LOGCONFIG("MAS3507D: %d:%04x = %05x\n", bank, adr, val); break;
}
@@ -411,8 +389,7 @@ void mas3507d_device::reg_write(uint32_t adr, uint32_t val)
case 0x8e: LOGCONFIG("MAS3507D: DCCF = %05x\n", val); break;
case 0xaa:
LOGCONFIG("MAS3507D: Mute/bypass = %05x\n", val);
- set_output_gain(0, val == 1 ? 0 : gain_ll);
- set_output_gain(1, val == 1 ? 0 : gain_rr);
+ is_muted = val == 1;
break;
case 0xe6: LOGCONFIG("MAS3507D: StartupConfig = %05x\n", val); break;
case 0xe7: LOGCONFIG("MAS3507D: Kprescale = %05x\n", val); break;
@@ -439,60 +416,54 @@ void mas3507d_device::sid_w(uint8_t byte)
mp3data[mp3data_count++] = byte;
- if (!mp3_is_buffered) {
- // Only start the decoder when a full MP3 frame is found
- int free_format_bytes = 0, frame_size = 0;
- int frame_offset = mp3d_find_frame(static_cast<const uint8_t *>(&mp3data[0]), mp3data_count, &free_format_bytes, &frame_size);
- mp3_is_buffered = frame_size && frame_offset + frame_size < mp3data_count;
- }
-
- cb_demand(!mp3_is_buffered || mp3data_count < mp3data.size());
+ cb_demand(mp3data_count < mp3data.size());
}
void mas3507d_device::fill_buffer()
{
cb_mpeg_frame_sync(0);
- if (!mp3_is_buffered) {
- cb_demand(!mp3_is_buffered || mp3data_count < mp3data.size());
- return;
- }
-
- memset(&mp3_info, 0, sizeof(mp3dec_frame_info_t));
- sample_count = mp3dec_decode_frame(&mp3_dec, static_cast<const uint8_t *>(&mp3data[0]), mp3data_count, static_cast<mp3d_sample_t *>(&samples[0]), &mp3_info);
+ int pos = 0, frame_sample_rate = 0;
+ bool decoded_frame = mp3dec->decode_buffer(pos, mp3data_count, &samples[0], sample_count, frame_sample_rate, frame_channels);
samples_idx = 0;
- if (sample_count == 0) {
+ if (!decoded_frame || sample_count == 0) {
// Frame decode failed
- reset_playback();
+ if (mp3data_count >= mp3data.size()) {
+ std::copy(mp3data.begin() + 1, mp3data.end(), mp3data.begin());
+ mp3data_count--;
+ }
+
+ cb_demand(1); // always request more data when nothing could be decoded to force potentially stale data out of the buffer
return;
}
- std::copy(mp3data.begin() + mp3_info.frame_bytes, mp3data.end(), mp3data.begin());
- mp3data_count -= mp3_info.frame_bytes;
+ std::copy(mp3data.begin() + pos, mp3data.end(), mp3data.begin());
+ mp3data_count -= pos;
- stream->set_sample_rate(mp3_info.hz);
+ stream->set_sample_rate(frame_sample_rate);
decoded_frame_count++;
cb_mpeg_frame_sync(1);
- cb_demand(!mp3_is_buffered || mp3data_count < mp3data.size());
+ cb_demand(mp3data_count < mp3data.size());
}
void mas3507d_device::append_buffer(std::vector<write_stream_view> &outputs, int &pos, int scount)
{
- int s1 = scount - pos;
- int bytes_per_sample = mp3_info.channels > 2 ? 2 : mp3_info.channels; // More than 2 channels is unsupported here
-
- if(s1 > sample_count)
- s1 = sample_count;
+ const int bytes_per_sample = std::min(frame_channels, 2); // More than 2 channels is unsupported here
+ const int s1 = std::min(scount - pos, sample_count);
+ const stream_buffer::sample_t sample_scale = 1.0 / 32768.0;
+ const stream_buffer::sample_t mute_scale = is_muted ? 0.0 : 1.0;
for(int i = 0; i < s1; i++) {
- outputs[0].put_int(pos, samples[samples_idx * bytes_per_sample], 32768);
- outputs[1].put_int(pos, samples[samples_idx * bytes_per_sample + (bytes_per_sample >> 1)], 32768);
+ const stream_buffer::sample_t lsamp_mixed = stream_buffer::sample_t(samples[samples_idx * bytes_per_sample]) * sample_scale * mute_scale * gain_ll;
+ const stream_buffer::sample_t rsamp_mixed = stream_buffer::sample_t(samples[samples_idx * bytes_per_sample + (bytes_per_sample >> 1)]) * sample_scale * mute_scale * gain_rr;
+
+ outputs[0].put(pos, lsamp_mixed);
+ outputs[1].put(pos, rsamp_mixed);
samples_idx++;
- decoded_samples++;
pos++;
if(samples_idx >= sample_count) {
@@ -504,20 +475,14 @@ void mas3507d_device::append_buffer(std::vector<write_stream_view> &outputs, int
void mas3507d_device::reset_playback()
{
- if (mp3data_count != 0)
- std::fill(mp3data.begin(), mp3data.end(), 0);
+ std::fill(mp3data.begin(), mp3data.end(), 0);
+ std::fill(samples.begin(), samples.end(), 0);
- if (sample_count != 0 || decoded_samples != 0)
- std::fill(samples.begin(), samples.end(), 0);
-
- mp3dec_init(&mp3_dec);
+ mp3dec->clear();
mp3data_count = 0;
sample_count = 0;
decoded_frame_count = 0;
- decoded_samples = 0;
samples_idx = 0;
-
- mp3_is_buffered = false;
}
void mas3507d_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
diff --git a/src/devices/sound/mas3507d.h b/src/devices/sound/mas3507d.h
index 52c2e924109..4402e8941d3 100644
--- a/src/devices/sound/mas3507d.h
+++ b/src/devices/sound/mas3507d.h
@@ -5,9 +5,7 @@
#pragma once
-#define MINIMP3_ONLY_MP3
-#define MINIMP3_NO_STDIO
-#include "minimp3/minimp3.h"
+#include "mp3_audio.h"
class mas3507d_device : public device_t, public device_sound_interface
{
@@ -25,8 +23,6 @@ public:
void sid_w(uint8_t byte);
- void update_stream() { stream->update(); }
-
void reset_playback();
protected:
@@ -72,13 +68,10 @@ private:
i2c_subdest_t i2c_subdest;
i2c_command_t i2c_command;
- mp3dec_t mp3_dec;
- mp3dec_frame_info_t mp3_info;
-
sound_stream *stream;
std::array<uint8_t, 0xe00> mp3data;
- std::array<mp3d_sample_t, MINIMP3_MAX_SAMPLES_PER_FRAME> samples;
+ std::array<short, 1152*2> samples;
bool i2c_scli, i2c_sclo, i2c_sdai, i2c_sdao;
int i2c_bus_curbit;
@@ -87,15 +80,15 @@ private:
uint32_t i2c_io_bank, i2c_io_adr, i2c_io_count, i2c_io_val;
uint32_t i2c_sdao_data;
- bool mp3_is_buffered;
uint32_t mp3data_count;
- uint32_t decoded_frame_count, decoded_samples;
+ uint32_t decoded_frame_count;
int32_t sample_count, samples_idx;
+ int32_t frame_channels;
bool is_muted;
float gain_ll, gain_rr;
- uint32_t playback_status;
+ std::unique_ptr<mp3_audio> mp3dec;
};
diff --git a/src/devices/sound/mea8000.cpp b/src/devices/sound/mea8000.cpp
index e557d046db4..25381b9ae81 100644
--- a/src/devices/sound/mea8000.cpp
+++ b/src/devices/sound/mea8000.cpp
@@ -130,8 +130,6 @@ mea8000_device::mea8000_device(const machine_config &mconfig, const char *tag, d
void mea8000_device::device_start()
{
- m_write_req.resolve_safe();
-
init_tables();
m_stream = stream_alloc(0, 1, clock() / 60);
diff --git a/src/devices/sound/meg.cpp b/src/devices/sound/meg.cpp
index f6a90812d6a..5d36f34c014 100644
--- a/src/devices/sound/meg.cpp
+++ b/src/devices/sound/meg.cpp
@@ -6,179 +6,23 @@
// Audio dsp dedicated to effects generation
#include "emu.h"
-#include "debugger.h"
#include "meg.h"
DEFINE_DEVICE_TYPE(MEG, meg_device, "meg", "Multiple Effects Generator (HD62098 / XM309A00)")
-DEFINE_DEVICE_TYPE(MEGEMB, meg_embedded_device, "megemb", "Multiple Effects Generator (embedded)")
-void meg_base_device::prg_map(address_map &map)
-{
- map(0, m_prg_size - 1).ram();
-}
-
-void meg_base_device::fp_map(address_map &map)
-{
- map(0, m_prg_size - 1).ram();
-}
-
-void meg_base_device::offsets_map(address_map &map)
-{
- map(0, 0x7f).ram();
-}
-
-meg_base_device::meg_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, u32 prg_size) :
- cpu_device(mconfig, type, tag, owner, clock),
- m_program_config("program", ENDIANNESS_BIG, 64, prg_size > 256 ? 9 : 8, -3, address_map_constructor(FUNC(meg_base_device::prg_map), this)),
- m_fp_config("fp", ENDIANNESS_BIG, 16, prg_size > 256 ? 9 : 8, -1, address_map_constructor(FUNC(meg_base_device::fp_map), this)),
- m_offsets_config("offsets", ENDIANNESS_BIG, 16, prg_size > 256 ? 7 : 7, -1, address_map_constructor(FUNC(meg_base_device::offsets_map), this)),
- m_prg_size(prg_size)
-{
-}
-
-
-void meg_base_device::prg_w(u16 address, u64 opcode)
-{
- m_program->write_qword(address, opcode);
-}
-
-void meg_base_device::fp_w(u16 address, u16 value)
-{
- m_fp->write_word(address, value);
-}
-
-void meg_base_device::offset_w(u16 address, u16 value)
-{
- m_offsets->write_word(address, value);
-}
-
-void meg_base_device::lfo_w(u8 reg, u16 value)
-{
- m_lfo[reg] = value;
-
- static const int dt[8] = { 0, 32, 64, 128, 256, 512, 1024, 2048 };
- static const int sh[8] = { 0, 0, 1, 2, 3, 4, 5, 6 };
-
- int scale = (value >> 5) & 7;
- int step = ((value & 31) << sh[scale]) + dt[scale];
- logerror("lfo_w %02x freq=%5.2f phase=%6.4f\n", reg, step * 44100.0/4194304, (value >> 8)/256.0);
-}
-
-void meg_base_device::map_w(u8 reg, u16 value)
-{
- m_map[reg] = value;
- logerror("map %d: start = %06x size = %06x extra = %x\n", reg, (value & 0xff) << 10, 1 << (10 + ((value & 0x0700) >> 8)), (value & 0xf800) >> 11);
-}
-
-u64 meg_base_device::prg_r(u16 address) const
-{
- return m_program->read_qword(address);
-}
-
-u16 meg_base_device::fp_r(u16 address) const
-{
- return m_fp->read_word(address);
-}
-
-u16 meg_base_device::offset_r(u16 address) const
-{
- return m_offsets->read_word(address);
-}
-
-u16 meg_base_device::lfo_r(u8 reg) const
-{
- return m_lfo[reg];
-}
-
-u16 meg_base_device::map_r(u8 reg) const
-{
- return m_map[reg];
-}
-
-
-void meg_base_device::device_start()
-{
- m_program = &space(AS_PROGRAM);
- m_fp = &space(AS_FP);
- m_offsets = &space(AS_OFFSETS);
-
- state_add(STATE_GENPC, "GENPC", m_pc).noshow();
- state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow();
- state_add(0, "PC", m_pc);
-
- set_icountptr(m_icount);
-
- save_item(NAME(m_lfo));
- save_item(NAME(m_map));
- save_item(NAME(m_pc));
-}
-
-void meg_base_device::device_reset()
-{
- memset(m_lfo, 0, sizeof(m_lfo));
- memset(m_map, 0, sizeof(m_map));
- m_pc = 0;
-}
-
-uint32_t meg_base_device::execute_min_cycles() const noexcept
-{
- return 1;
-}
-
-uint32_t meg_base_device::execute_max_cycles() const noexcept
-{
- return 1;
-}
-
-uint32_t meg_base_device::execute_input_lines() const noexcept
-{
- return 0;
-}
-
-void meg_base_device::execute_run()
-{
- if(machine().debug_flags & DEBUG_FLAG_ENABLED)
- debugger_instruction_hook(m_pc);
- m_icount = 0;
-}
-
-device_memory_interface::space_config_vector meg_base_device::memory_space_config() const
-{
- return space_config_vector {
- std::make_pair(AS_PROGRAM, &m_program_config),
- std::make_pair(AS_FP, &m_fp_config),
- std::make_pair(AS_OFFSETS, &m_offsets_config)
- };
-}
-
-void meg_base_device::state_import(const device_state_entry &entry)
-{
-}
-
-void meg_base_device::state_export(const device_state_entry &entry)
-{
-}
-
-void meg_base_device::state_string_export(const device_state_entry &entry, std::string &str) const
-{
-}
-
-std::unique_ptr<util::disasm_interface> meg_base_device::create_disassembler()
+meg_device::meg_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, MEG, tag, owner, clock)
{
- return std::make_unique<meg_disassembler>(this);
}
-meg_embedded_device::meg_embedded_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
- meg_base_device(mconfig, MEGEMB, tag, owner, clock, 384)
+void meg_device::device_start()
{
}
-meg_device::meg_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
- meg_base_device(mconfig, MEG, tag, owner, clock, 256)
+void meg_device::device_reset()
{
}
-
// vl70:
// 6d1e: write 1, r0l
// 6d26: write 2, r0l
@@ -213,8 +57,8 @@ void meg_device::map(address_map &map)
map(0x08, 0x08).w(FUNC(meg_device::s8_w));
map(0x09, 0x09).w(FUNC(meg_device::s9_w));
map(0x0a, 0x0a).w(FUNC(meg_device::sa_w));
- map(0x0c, 0x0c).w(FUNC(meg_device::fph_w));
- map(0x0d, 0x0d).w(FUNC(meg_device::fpl_w));
+ map(0x0c, 0x0c).w(FUNC(meg_device::consth_w));
+ map(0x0d, 0x0d).w(FUNC(meg_device::constl_w));
map(0x0e, 0x0e).w(FUNC(meg_device::se_w));
map(0x0f, 0x0f).w(FUNC(meg_device::sf_w));
map(0x10, 0x10).r(FUNC(meg_device::s10_r));
@@ -297,15 +141,15 @@ void meg_device::sa_w(u8 data)
logerror("ra %02x %s\n", data, machine().describe_context());
}
-void meg_device::fph_w(u8 data)
+void meg_device::consth_w(u8 data)
{
- fp_w(m_reg, (fp_r(m_reg) & 0x00ff) | (data << 8));
+ m_const[m_reg] = (m_const[m_reg] & 0x00ff) | (data << 8);
}
-void meg_device::fpl_w(u8 data)
+void meg_device::constl_w(u8 data)
{
- fp_w(m_reg, (fp_r(m_reg) & 0xff00) | data);
+ m_const[m_reg] = (m_const[m_reg] & 0xff00) | data;
}
void meg_device::se_w(u8 data)
@@ -339,12 +183,12 @@ u8 meg_device::s11_r()
void meg_device::offseth_w(u8 data)
{
- offset_w(m_reg, (offset_r(m_reg) & 0x00ff) | (data << 8));
+ m_offset[m_reg] = (m_offset[m_reg] & 0x00ff) | (data << 8);
}
void meg_device::offsetl_w(u8 data)
{
- offset_w(m_reg, (offset_r(m_reg) & 0xff00) | data);
+ m_offset[m_reg] = (m_offset[m_reg] & 0xff00) | data;
}
void meg_device::s14_w(u8 data)
diff --git a/src/devices/sound/meg.h b/src/devices/sound/meg.h
index 40129158fc8..f10cb08b228 100644
--- a/src/devices/sound/meg.h
+++ b/src/devices/sound/meg.h
@@ -3,75 +3,22 @@
// Yamaha MEG - Multiple effects generator
//
-// Audio dsp dedicated to effects generation
+// Audio dsp dedicated to effects generation, part of the SWP20 lineup
#ifndef DEVICES_SOUND_MEG_H
#define DEVICES_SOUND_MEG_H
#pragma once
-#include "megd.h"
-
-
-class meg_base_device : public cpu_device, public meg_disassembler::info
+class meg_device : public device_t
{
public:
- enum {
- AS_FP = 1,
- AS_OFFSETS = 2
- };
-
- meg_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, u32 prg_size);
-
- void prg_w(u16 address, u64 opcode);
- void fp_w(u16 address, u16 value);
- void offset_w(u16 address, u16 value);
- void lfo_w(u8 reg, u16 value);
- void map_w(u8 reg, u16 value);
- u64 prg_r(u16 address) const;
- virtual u16 fp_r(u16 address) const override;
- virtual u16 offset_r(u16 address) const override;
- u16 lfo_r(u8 reg) const;
- u16 map_r(u8 reg) const;
+ meg_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 44100*256);
+ void map(address_map &map);
protected:
virtual void device_start() override;
virtual void device_reset() override;
- virtual uint32_t execute_min_cycles() const noexcept override;
- virtual uint32_t execute_max_cycles() const noexcept override;
- virtual uint32_t execute_input_lines() const noexcept override;
- virtual void execute_run() override;
- virtual space_config_vector memory_space_config() const override;
- virtual void state_import(const device_state_entry &entry) override;
- virtual void state_export(const device_state_entry &entry) override;
- virtual void state_string_export(const device_state_entry &entry, std::string &str) const override;
- virtual std::unique_ptr<util::disasm_interface> create_disassembler() override;
-
-private:
- address_space_config m_program_config, m_fp_config, m_offsets_config;
- address_space *m_program, *m_fp, *m_offsets;
-
- u32 m_prg_size, m_pc;
- int m_icount;
-
- u16 m_lfo[0x18], m_map[8];
-
- void prg_map(address_map &map);
- void fp_map(address_map &map);
- void offsets_map(address_map &map);
-};
-
-class meg_embedded_device : public meg_base_device
-{
-public:
- meg_embedded_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 44100*384);
-};
-
-class meg_device : public meg_base_device
-{
-public:
- meg_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 44100*256);
- void map(address_map &map);
private:
u8 m_r4[256];
@@ -87,6 +34,10 @@ private:
u8 m_r17[256];
u8 m_r18[256];
u8 m_reg;
+
+ std::array<s16, 0x100> m_const;
+ std::array<s16, 0x100> m_offset;
+
u8 s2_r();
u8 s10_r();
u8 s11_r();
@@ -100,8 +51,8 @@ private:
void s8_w(u8 data);
void s9_w(u8 data);
void sa_w(u8 data);
- void fph_w(u8 data);
- void fpl_w(u8 data);
+ void consth_w(u8 data);
+ void constl_w(u8 data);
void se_w(u8 data);
void sf_w(u8 data);
void s10_w(u8 data);
@@ -115,8 +66,6 @@ private:
void s18_w(u8 data);
};
-
DECLARE_DEVICE_TYPE(MEG, meg_device)
-DECLARE_DEVICE_TYPE(MEGEMB, meg_embedded_device)
#endif
diff --git a/src/devices/sound/megd.cpp b/src/devices/sound/megd.cpp
deleted file mode 100644
index 47ff6dfdb3d..00000000000
--- a/src/devices/sound/megd.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-// license:BSD-3-Clause
-// copyright-holders:Olivier Galibert
-
-// Yamaha MEG - Multiple effects generator
-//
-// Audio dsp dedicated to effects generation
-//
-// Disassembler
-
-#include "emu.h"
-#include "megd.h"
-
-meg_disassembler::meg_disassembler(info *inf) : m_info(inf)
-{
-}
-
-u32 meg_disassembler::opcode_alignment() const
-{
- return 1;
-}
-
-std::string meg_disassembler::gfp(offs_t address) const
-{
- if(!m_info)
- return util::string_format("fp%03x", address);
- s16 fp = m_info->fp_r(address);
- return util::string_format("%g", fp / 16384.0);
-}
-
-std::string meg_disassembler::goffset(offs_t address) const
-{
- return m_info ? util::string_format("%x", m_info->offset_r(address)) : util::string_format("of%02x", address);
-}
-
-u32 meg_disassembler::b(u64 opc, u32 start, u32 count)
-{
- return (opc >> start) & ((1 << count) - 1);
-}
-
-void meg_disassembler::append(std::string &r, const std::string &e)
-{
- if(r != "")
- r += " ; ";
- r += e;
-}
-
-// 33333333 33333333 22222222 22222222 11111111 11111111 00000000 00000000
-// fedcba98 76543210 fedcba98 76543210 fedcba98 76543210 fedcba98 76543210
-
-// 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000
-// 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210
-// XLB----- -rrrrrrr r--mmmmm m-MM---- -P-----* -----Arr rrrrrrmm mmmm----
-
-// m = low is read port, high is write port, memory register
-// r = low is read port, high is high port, rotating register
-
-// X = used for lo-fi variation only
-// L = lfo read
-// * = compute mul
-// A = mul input = m or r
-// P = P sent for register write
-// B = register write to mbuf
-// M = memory mode, none/read/write/read+1
-
-offs_t meg_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params)
-{
- u64 opc = opcodes.r64(pc);
-
- std::string r;
-
- r = util::string_format("[m%02x]", b(opc, 39, 6));
-
- if(b(opc, 62, 1))
- append(r, "lfo");
-
- if(b(opc, 23, 1))
- switch(b(opc, 24, 2)) {
- case 0:
- if(b(opc, 18, 1))
- append(r, util::string_format("p += %s*m%02x", gfp(pc), b(opc, 4, 6)));
- else
- append(r, util::string_format("p += %s*r%02x", gfp(pc), b(opc, 10, 8)));
- break;
- case 1:
- append(r, util::string_format("p = %s*(r%02x+m%02x)", gfp(pc), b(opc, 10, 8), b(opc, 4, 6)));
- break;
- case 2:
- append(r, util::string_format("p ?= %s*(r%02x+m%02x)", gfp(pc), b(opc, 10, 8), b(opc, 4, 6)));
- break;
- case 3:
- if(b(opc, 18, 1))
- append(r, util::string_format("p = %s*m%02x", gfp(pc), b(opc, 4, 6)));
- else
- append(r, util::string_format("p = %s*r%02x", gfp(pc), b(opc, 10, 8)));
- break;
- }
-
- if(b(opc, 30, 1)) {
- if(b(opc, 61, 1))
- append(r, "mb = p");
- else if(b(opc, 46, 1) == 1)
- append(r, util::string_format("m%02x = p", b(opc, 39, 6)));
- else
- append(r, util::string_format("r%02x = p", b(opc, 47, 8)));
- }
-
- u32 memmode = b(opc, 36, 2);
- if(memmode) {
- static const char *modes[4] = { nullptr, "w", "r", "rw" };
-
- append(r, util::string_format("mem_%s %x +%s", modes[memmode], b(opc, 33, 3), goffset(pc/3)));
- r += util::string_format("-> m%02x", b(opcodes.r64(pc+2), 39, 6));
- }
-
- stream << r;
-
- return 1 | SUPPORTED;
-}
diff --git a/src/devices/sound/mixer.cpp b/src/devices/sound/mixer.cpp
index 3b8435182c4..ba57be9b10c 100644
--- a/src/devices/sound/mixer.cpp
+++ b/src/devices/sound/mixer.cpp
@@ -27,4 +27,6 @@ mixer_device::mixer_device(const machine_config &mconfig, const char *tag, devic
void mixer_device::device_start()
{
+ // register for save states
+ save_item(NAME(m_dummy));
}
diff --git a/src/devices/sound/mixer.h b/src/devices/sound/mixer.h
index e25466ea351..5be1d45d5d3 100644
--- a/src/devices/sound/mixer.h
+++ b/src/devices/sound/mixer.h
@@ -22,6 +22,9 @@ public:
protected:
// device-level overrides
virtual void device_start() override;
+
+private:
+ u8 m_dummy = 0; // needed for save-state support
};
#endif // MAME_SOUND_MIXER_H
diff --git a/src/devices/sound/mm5837.cpp b/src/devices/sound/mm5837.cpp
index ae6687655f3..40bec2727b8 100644
--- a/src/devices/sound/mm5837.cpp
+++ b/src/devices/sound/mm5837.cpp
@@ -43,9 +43,6 @@ mm5837_device::mm5837_device(const machine_config &mconfig, const char *tag, dev
void mm5837_device::device_start()
{
- // resolve callbacks
- m_output_cb.resolve_safe();
-
// get timer
m_timer = timer_alloc(FUNC(mm5837_device::update_clock_output), this);
diff --git a/src/devices/sound/mos6560.cpp b/src/devices/sound/mos6560.cpp
index bd35429c31c..6434621f13d 100644
--- a/src/devices/sound/mos6560.cpp
+++ b/src/devices/sound/mos6560.cpp
@@ -59,7 +59,7 @@
#include "emu.h"
-#include "sound/mos6560.h"
+#include "mos6560.h"
/*****************************************************************************
@@ -325,6 +325,8 @@ void mos6560_device::drawlines( int first, int last )
void mos6560_device::write(offs_t offset, uint8_t data)
{
+ offset &= 0xf;
+
DBG_LOG(1, "mos6560_port_w", ("%.4x:%.2x\n", offset, data));
switch (offset)
@@ -403,6 +405,8 @@ void mos6560_device::write(offs_t offset, uint8_t data)
uint8_t mos6560_device::read(offs_t offset)
{
+ offset &= 0xf;
+
int val;
switch (offset)
@@ -680,16 +684,16 @@ void mos6560_device::mos6560_colorram_map(address_map &map)
map(0x000, 0x3ff).ram();
}
-mos6560_device::mos6560_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint32_t variant)
- : device_t(mconfig, type, tag, owner, clock),
- device_memory_interface(mconfig, *this),
- device_sound_interface(mconfig, *this),
- device_video_interface(mconfig, *this),
- m_variant(variant),
- m_videoram_space_config("videoram", ENDIANNESS_LITTLE, 8, 14, 0, address_map_constructor(FUNC(mos6560_device::mos6560_videoram_map), this)),
- m_colorram_space_config("colorram", ENDIANNESS_LITTLE, 8, 10, 0, address_map_constructor(FUNC(mos6560_device::mos6560_colorram_map), this)),
- m_read_potx(*this),
- m_read_poty(*this)
+mos6560_device::mos6560_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint32_t variant) :
+ device_t(mconfig, type, tag, owner, clock),
+ device_memory_interface(mconfig, *this),
+ device_sound_interface(mconfig, *this),
+ device_video_interface(mconfig, *this),
+ m_variant(variant),
+ m_videoram_space_config("videoram", ENDIANNESS_LITTLE, 8, 14, 0, address_map_constructor(FUNC(mos6560_device::mos6560_videoram_map), this)),
+ m_colorram_space_config("colorram", ENDIANNESS_LITTLE, 8, 10, 0, address_map_constructor(FUNC(mos6560_device::mos6560_colorram_map), this)),
+ m_read_potx(*this, 0xff),
+ m_read_poty(*this, 0xff)
{
}
@@ -731,10 +735,6 @@ void mos6560_device::device_start()
{
screen().register_screen_bitmap(m_bitmap);
- // resolve callbacks
- m_read_potx.resolve_safe(0xff);
- m_read_poty.resolve_safe(0xff);
-
switch (m_variant)
{
case TYPE_6560:
diff --git a/src/devices/sound/mos6581.cpp b/src/devices/sound/mos6581.cpp
index 6682ddca4fe..941472a58f0 100644
--- a/src/devices/sound/mos6581.cpp
+++ b/src/devices/sound/mos6581.cpp
@@ -41,8 +41,8 @@ DEFINE_DEVICE_TYPE(MOS8580, mos8580_device, "mos8580", "MOS 8580 SID")
mos6581_device::mos6581_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint32_t variant)
: device_t(mconfig, type, tag, owner, clock)
, device_sound_interface(mconfig, *this)
- , m_read_potx(*this)
- , m_read_poty(*this)
+ , m_read_potx(*this, 0xff)
+ , m_read_poty(*this, 0xff)
, m_stream(nullptr)
, m_variant(variant)
@@ -193,10 +193,6 @@ void mos6581_device::save_state(SID6581_t *token)
void mos6581_device::device_start()
{
- // resolve callbacks
- m_read_potx.resolve_safe(0xff);
- m_read_poty.resolve_safe(0xff);
-
// create sound stream
m_stream = stream_alloc(0, 1, machine().sample_rate());
diff --git a/src/devices/sound/mos7360.cpp b/src/devices/sound/mos7360.cpp
index c76e1d8adb7..51953d93ecd 100644
--- a/src/devices/sound/mos7360.cpp
+++ b/src/devices/sound/mos7360.cpp
@@ -261,15 +261,15 @@ inline uint8_t mos7360_device::read_rom(offs_t offset)
// mos7360_device - constructor
//-------------------------------------------------
-mos7360_device::mos7360_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, MOS7360, tag, owner, clock),
- device_memory_interface(mconfig, *this),
- device_sound_interface(mconfig, *this),
- device_video_interface(mconfig, *this),
- m_videoram_space_config("videoram", ENDIANNESS_LITTLE, 8, 16, 0, address_map_constructor(FUNC(mos7360_device::mos7360_videoram_map), this)),
- m_write_irq(*this),
- m_read_k(*this),
- m_stream(nullptr)
+mos7360_device::mos7360_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, MOS7360, tag, owner, clock),
+ device_memory_interface(mconfig, *this),
+ device_sound_interface(mconfig, *this),
+ device_video_interface(mconfig, *this),
+ m_videoram_space_config("videoram", ENDIANNESS_LITTLE, 8, 16, 0, address_map_constructor(FUNC(mos7360_device::mos7360_videoram_map), this)),
+ m_write_irq(*this),
+ m_read_k(*this, 0xff),
+ m_stream(nullptr)
{
}
@@ -280,10 +280,6 @@ mos7360_device::mos7360_device(const machine_config &mconfig, const char *tag, d
void mos7360_device::device_start()
{
- // resolve callbacks
- m_write_irq.resolve_safe();
- m_read_k.resolve_safe(0xff);
-
// allocate timers
m_timer[TIMER_ID_1] = timer_alloc(FUNC(mos7360_device::timer_expired), this);
m_timer[TIMER_ID_2] = timer_alloc(FUNC(mos7360_device::timer_expired), this);
diff --git a/src/devices/sound/mp3_audio.cpp b/src/devices/sound/mp3_audio.cpp
new file mode 100644
index 00000000000..c5ac8797195
--- /dev/null
+++ b/src/devices/sound/mp3_audio.cpp
@@ -0,0 +1,90 @@
+// license:BSD-3-Clause
+// copyright-holders:windyfairy
+/***************************************************************************
+
+ MP3 audio decoder
+
+***************************************************************************/
+
+#include "emu.h"
+#include "mp3_audio.h"
+
+#define MINIMP3_IMPLEMENTATION
+#define MAX_FRAME_SYNC_MATCHES 2
+#include "minimp3/minimp3.h"
+
+// To avoid modifying minimp3.h, forward declare mp3dec_local_t in mp3_audio.h and then make it an mp3dec_t using inheritance
+struct mp3_audio::mp3dec_local_t : public mp3dec_t
+{
+};
+
+mp3_audio::mp3_audio(const void *_base)
+ : base((const uint8_t *)_base)
+{
+ dec = std::make_unique<mp3dec_local_t>();
+ clear();
+}
+
+mp3_audio::~mp3_audio()
+{
+}
+
+void mp3_audio::register_save(device_t &host)
+{
+ host.save_item(NAME(m_found_stream));
+ host.save_item(NAME(dec->header));
+ host.save_item(NAME(dec->reserv_buf));
+ host.save_item(NAME(dec->mdct_overlap));
+ host.save_item(NAME(dec->qmf_state));
+ host.save_item(NAME(dec->reserv));
+ host.save_item(NAME(dec->free_format_bytes));
+}
+
+void mp3_audio::clear()
+{
+ mp3dec_init(dec.get());
+ m_found_stream = false;
+}
+
+bool mp3_audio::decode_buffer(int &pos, int limit, short *output, int &output_samples, int &sample_rate, int &channels)
+{
+ mp3dec_frame_info_t info = {};
+
+ if (!m_found_stream)
+ {
+ // Guarantee a specified number of frames are buffered before starting decoding to ensure it's not full of garbage that looks like a valid frame
+ int free_format_bytes = 0;
+ int frame_bytes = 0;
+ int frame_offset = mp3d_find_frame(base, limit, &free_format_bytes, &frame_bytes);
+
+ if (frame_bytes && frame_offset + frame_bytes <= limit)
+ {
+ int i = 0, nmatch = 0;
+
+ for (i = frame_offset, nmatch = 0; nmatch < MAX_FRAME_SYNC_MATCHES; nmatch++)
+ {
+ i += hdr_frame_bytes(base + i, frame_bytes) + hdr_padding(base + i);
+ if (i + HDR_SIZE > limit || !hdr_compare(base + frame_offset, base + i))
+ break;
+ }
+
+ m_found_stream = nmatch >= MAX_FRAME_SYNC_MATCHES;
+ }
+
+ if (!m_found_stream)
+ {
+ output_samples = 0;
+ sample_rate = 0;
+ channels = 0;
+ pos = 0;
+ return false;
+ }
+ }
+
+ output_samples = mp3dec_decode_frame(dec.get(), base, limit, output, &info);
+ sample_rate = info.hz;
+ channels = info.channels;
+ pos = info.frame_bytes;
+
+ return pos > 0 && output_samples > 0;
+}
diff --git a/src/devices/sound/mp3_audio.h b/src/devices/sound/mp3_audio.h
new file mode 100644
index 00000000000..ffafdb1f219
--- /dev/null
+++ b/src/devices/sound/mp3_audio.h
@@ -0,0 +1,37 @@
+// license:BSD-3-Clause
+// copyright-holders:windyfairy
+/***************************************************************************
+
+ MP3 audio decoder
+
+***************************************************************************/
+
+#ifndef MAME_SOUND_MP3_AUDIO_H
+#define MAME_SOUND_MP3_AUDIO_H
+
+#pragma once
+
+#include <stdint.h>
+
+class mp3_audio
+{
+public:
+ mp3_audio(const void *base);
+ ~mp3_audio();
+
+ void register_save(device_t &host);
+
+ bool decode_buffer(int &pos, int limit, short *output, int &output_samples, int &sample_rate, int &channels);
+
+ void clear();
+
+private:
+ struct mp3dec_local_t;
+
+ const uint8_t *base;
+
+ std::unique_ptr<mp3dec_local_t> dec;
+ bool m_found_stream;
+};
+
+#endif
diff --git a/src/devices/sound/msm5205.cpp b/src/devices/sound/msm5205.cpp
index ba196a419a3..f3f1f5e6302 100644
--- a/src/devices/sound/msm5205.cpp
+++ b/src/devices/sound/msm5205.cpp
@@ -91,9 +91,6 @@ msm6585_device::msm6585_device(const machine_config &mconfig, const char *tag, d
void msm5205_device::device_start()
{
- m_vck_cb.resolve_safe();
- m_vck_legacy_cb.resolve();
-
/* compute the difference tables */
compute_tables();
@@ -190,7 +187,7 @@ TIMER_CALLBACK_MEMBER(msm5205_device::update_adpcm)
int new_signal;
// callback user handler and latch next data
- if (!m_vck_legacy_cb.isnull())
+ if (!m_vck_legacy_cb.isunset())
m_vck_legacy_cb(1);
// reset check at last hiedge of VCK
diff --git a/src/devices/sound/msm5232.cpp b/src/devices/sound/msm5232.cpp
index 3d08f3582a8..3761e442ae7 100644
--- a/src/devices/sound/msm5232.cpp
+++ b/src/devices/sound/msm5232.cpp
@@ -30,8 +30,6 @@ void msm5232_device::device_start()
int rate = clock()/CLOCK_RATE_DIVIDER;
int voicenum;
- m_gate_handler_cb.resolve();
-
init(clock(), rate);
m_stream = stream_alloc(0, 11, rate);
@@ -110,7 +108,7 @@ void msm5232_device::device_reset()
void msm5232_device::device_stop()
{
- #ifdef SAVE_SAMPLE
+#ifdef SAVE_SAMPLE
fclose(sample[8]);
#endif
#ifdef SAVE_SEPARATE_CHANNELS
@@ -127,22 +125,22 @@ void msm5232_device::device_stop()
void msm5232_device::set_capacitors(double cap1, double cap2, double cap3, double cap4, double cap5, double cap6, double cap7, double cap8)
{
- m_external_capacity[0] = cap1;
- m_external_capacity[1] = cap2;
- m_external_capacity[2] = cap3;
- m_external_capacity[3] = cap4;
- m_external_capacity[4] = cap5;
- m_external_capacity[5] = cap6;
- m_external_capacity[6] = cap7;
- m_external_capacity[7] = cap8;
+ m_external_capacitance[0] = cap1;
+ m_external_capacitance[1] = cap2;
+ m_external_capacitance[2] = cap3;
+ m_external_capacitance[3] = cap4;
+ m_external_capacitance[4] = cap5;
+ m_external_capacitance[5] = cap6;
+ m_external_capacitance[6] = cap7;
+ m_external_capacitance[7] = cap8;
}
-/* Default chip clock is 2119040 Hz */
-/* At this clock chip generates exactly 440.0 Hz signal on 8' output when pitch data=0x21 */
+// Default chip clock is 2119040 Hz
+// At this clock chip generates exactly 440.0 Hz signal on 8' output when pitch data=0x21
-/* ROM table to convert from pitch data into data for programmable counter and binary counter */
-/* Chip has 88x12bits ROM (addressing (in hex) from 0x00 to 0x57) */
+// ROM table to convert from pitch data into data for programmable counter and binary counter
+// Chip has 88x12bits ROM (addressing (in hex) from 0x00 to 0x57)
#define ROM(counter,bindiv) (counter|(bindiv<<9))
static const uint16_t MSM5232_ROM[88]={
@@ -199,87 +197,63 @@ static FILE *sample[9];
/*
- * resistance values are guesswork, default capacity is mentioned in the datasheets
+ * Resistance values are guesswork, default capacitance is mentioned in the datasheets
+ *
+ * Two errors in the datasheet, one probable, one certain
+ * - it mentions 0.39uF caps, but most boards have 1uF caps and expect datasheet timings
+ *
+ * - the 330ms timing of decay2 has been measured to be 250ms (which
+ * also matches the duty cycle information for the rest of the table)
+ *
+ * In both cases it ends up with smaller resistor values, which are
+ * easier to do on-die.
+ *
+ * The timings are for a 90% charge/discharge of the external
+ * capacitor through three possible resistors, one for attack, two for
+ * decay.
*
- * charges external capacitor (default is 0.39uF) via R51
- * in approx. 5*1400 * 0.39e-6
+ * Expected timings are 2ms, 40ms and 250ms respectively with a 1uF
+ * capacitor.
*
- * external capacitor is discharged through R52
- * in approx. 5*28750 * 0.39e-6
+ * exp(-t/(r*c)) = (100% - 90%) => r = -r/(log(0.1)*c)
+ *
+ * 2ms -> 870 ohms
+ * 40ms -> 17400 ohms
+ * 250ms -> 101000 ohms
*/
-#define R51 1400 /* charge resistance */
-#define R52 28750 /* discharge resistance */
-
-#if 0
-/*
- C24 = external capacity
-
- osd_printf_debug("Time constant T=R*C =%f sec.\n",R51*C24);
- osd_printf_debug("Cap fully charged after 5T=%f sec (sample=%f). Level=%f\n",(R51*C24)*5,(R51*C24)*5*sample_rate , VMAX*0.99326 );
- osd_printf_debug("Cap charged after 5T=%f sec (sample=%f). Level=%20.16f\n",(R51*C24)*5,(R51*C24)*5*sample_rate ,
- VMAX*(1.0-pow(2.718,-0.0748/(R51*C24))) );
-*/
-#endif
-
-
+static constexpr double R51 = 870; // attack resistance
+static constexpr double R52 = 17400; // decay 1 resistance
+static constexpr double R53 = 101000; // decay 2 resistance
void msm5232_device::init_tables()
{
- int i;
- double scale;
-
- /* sample rate = chip clock !!! But : */
- /* highest possible frequency is chipclock/13/16 (pitch data=0x57) */
- /* at 2MHz : 2000000/13/16 = 9615 Hz */
-
- i = ((double)(1<<STEP_SH) * (double)m_rate) / (double)m_chip_clock;
- m_UpdateStep = i;
- /* logerror("clock=%i Hz rate=%i Hz, UpdateStep=%i\n",
- m_chip_clock, m_rate, m_UpdateStep); */
+ // sample rate = chip clock !!! But :
+ // highest possible frequency is chipclock/13/16 (pitch data=0x57)
+ // at 2MHz : 2000000/13/16 = 9615 Hz
- scale = ((double)m_chip_clock) / (double)m_rate;
- m_noise_step = ((1<<STEP_SH)/128.0) * scale; /* step of the rng reg in 16.16 format */
- /* logerror("noise step=%8x\n", m_noise_step); */
-
-#if 0
-{
- /* rate tables (in milliseconds) */
- static const int ATBL[8] = { 2,4,8,16, 32,64, 32,64};
- static const int DTBL[16]= { 40,80,160,320, 640,1280, 640,1280,
- 333,500,1000,2000, 4000,8000, 4000,8000};
- for (i=0; i<8; i++)
- {
- double clockscale = (double)m_chip_clock / 2119040.0;
- double time = (ATBL[i] / 1000.0) / clockscale; /* attack time in seconds */
- m_ar_tbl[i] = 0.50 * ( (1.0/time) / (double)m_rate );
- /* logerror("ATBL[%i] = %20.16f time = %f s\n",i, m_ar_tbl[i], time); */
- }
-
- for (i=0; i<16; i++)
- {
- double clockscale = (double)m_chip_clock / 2119040.0;
- double time = (DTBL[i] / 1000.0) / clockscale; /* decay time in seconds */
- m_dr_tbl[i] = 0.50 * ( (1.0/time) / (double)m_rate );
- /* logerror("DTBL[%i] = %20.16f time = %f s\n",i, m_dr_tbl[i], time); */
- }
-}
-#endif
+ m_UpdateStep = int(double(1 << STEP_SH) * double(m_rate) / double(m_chip_clock));
+ //logerror("clock=%i Hz rate=%i Hz, UpdateStep=%i\n", m_chip_clock, m_rate, m_UpdateStep);
+ double const scale = double(m_chip_clock) / double(m_rate);
+ m_noise_step = ((1 << STEP_SH) / 128.0) * scale; // step of the rng reg in 16.16 format
+ //logerror("noise step=%8x\n", m_noise_step);
- for (i=0; i<8; i++)
+ for (int i = 0; i < 8; i++)
{
- double clockscale = (double)m_chip_clock / 2119040.0;
- m_ar_tbl[i] = ((1<<i) / clockscale) * (double)R51;
+ double const clockscale = double(m_chip_clock) / 2119040.0;
+ int const rcp_duty_cycle = 1 << ((i & 4) ? (i & ~2) : i); // bit 1 is ignored if bit 2 is set
+ m_ar_tbl[i] = (rcp_duty_cycle / clockscale) * R51;
}
- for (i=0; i<8; i++)
+ for (int i = 0; i < 8; i++)
{
- double clockscale = (double)m_chip_clock / 2119040.0;
- m_dr_tbl[i] = ( (1<<i) / clockscale) * (double)R52;
- m_dr_tbl[i+8] = (6.25*(1<<i) / clockscale) * (double)R52;
+ double const clockscale = double(m_chip_clock) / 2119040.0;
+ int const rcp_duty_cycle = 1 << ((i & 4) ? (i & ~2) : i); // bit 1 is ignored if bit 2 is set
+ m_dr_tbl[i] = (rcp_duty_cycle / clockscale) * R52;
+ m_dr_tbl[i + 8] = (rcp_duty_cycle / clockscale) * R53;
}
@@ -301,9 +275,9 @@ void msm5232_device::init_tables()
void msm5232_device::init_voice(int i)
{
- m_voi[i].ar_rate= m_ar_tbl[0] * m_external_capacity[i];
- m_voi[i].dr_rate= m_dr_tbl[0] * m_external_capacity[i];
- m_voi[i].rr_rate= m_dr_tbl[0] * m_external_capacity[i]; /* this is constant value */
+ m_voi[i].ar_rate= m_ar_tbl[0] * m_external_capacitance[i];
+ m_voi[i].dr_rate= m_dr_tbl[0] * m_external_capacitance[i];
+ m_voi[i].rr_rate= m_dr_tbl[0] * m_external_capacitance[i]; /* this is constant value */
m_voi[i].eg_sect= -1;
m_voi[i].eg = 0.0;
m_voi[i].eg_arm = 0;
@@ -315,7 +289,7 @@ void msm5232_device::gate_update()
{
int new_state = (m_control2 & 0x20) ? m_voi[7].GF : 0;
- if (m_gate != new_state && !m_gate_handler_cb.isnull())
+ if (m_gate != new_state)
{
m_gate = new_state;
m_gate_handler_cb(new_state);
@@ -408,22 +382,22 @@ void msm5232_device::write(offs_t offset, uint8_t data)
{
case 0x08: /* group1 attack */
for (i=0; i<4; i++)
- m_voi[i].ar_rate = m_ar_tbl[data&0x7] * m_external_capacity[i];
+ m_voi[i].ar_rate = m_ar_tbl[data&0x7] * m_external_capacitance[i];
break;
case 0x09: /* group2 attack */
for (i=0; i<4; i++)
- m_voi[i+4].ar_rate = m_ar_tbl[data&0x7] * m_external_capacity[i+4];
+ m_voi[i+4].ar_rate = m_ar_tbl[data&0x7] * m_external_capacitance[i+4];
break;
case 0x0a: /* group1 decay */
for (i=0; i<4; i++)
- m_voi[i].dr_rate = m_dr_tbl[data&0xf] * m_external_capacity[i];
+ m_voi[i].dr_rate = m_dr_tbl[data&0xf] * m_external_capacitance[i];
break;
case 0x0b: /* group2 decay */
for (i=0; i<4; i++)
- m_voi[i+4].dr_rate = m_dr_tbl[data&0xf] * m_external_capacity[i+4];
+ m_voi[i+4].dr_rate = m_dr_tbl[data&0xf] * m_external_capacitance[i+4];
break;
case 0x0c: /* group1 control */
diff --git a/src/devices/sound/msm5232.h b/src/devices/sound/msm5232.h
index 552cd7669e3..56b562b2c76 100644
--- a/src/devices/sound/msm5232.h
+++ b/src/devices/sound/msm5232.h
@@ -6,8 +6,7 @@
#pragma once
-class msm5232_device : public device_t,
- public device_sound_interface
+class msm5232_device : public device_t, public device_sound_interface
{
public:
msm5232_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
@@ -35,24 +34,24 @@ private:
int TG_count_period;
int TG_count;
- uint8_t TG_cnt; /* 7 bits binary counter (frequency output) */
- uint8_t TG_out16; /* bit number (of TG_cnt) for 16' output */
- uint8_t TG_out8; /* bit number (of TG_cnt) for 8' output */
- uint8_t TG_out4; /* bit number (of TG_cnt) for 4' output */
- uint8_t TG_out2; /* bit number (of TG_cnt) for 2' output */
+ uint8_t TG_cnt; // 7 bits binary counter (frequency output)
+ uint8_t TG_out16; // bit number (of TG_cnt) for 16' output
+ uint8_t TG_out8; // bit number (of TG_cnt) for 8' output
+ uint8_t TG_out4; // bit number (of TG_cnt) for 4' output
+ uint8_t TG_out2; // bit number (of TG_cnt) for 2' output
int egvol;
int eg_sect;
int counter;
int eg;
- uint8_t eg_arm; /* attack/release mode */
+ uint8_t eg_arm; // attack/release mode
double ar_rate;
double dr_rate;
double rr_rate;
- int pitch; /* current pitch data */
+ int pitch; // current pitch data
int GF;
};
@@ -62,32 +61,32 @@ private:
VOICE m_voi[8];
- uint32_t m_EN_out16[2]; /* enable 16' output masks for both groups (0-disabled ; ~0 -enabled) */
- uint32_t m_EN_out8[2]; /* enable 8' output masks */
- uint32_t m_EN_out4[2]; /* enable 4' output masks */
- uint32_t m_EN_out2[2]; /* enable 2' output masks */
+ uint32_t m_EN_out16[2]; // enable 16' output masks for both groups (0-disabled ; ~0 -enabled)
+ uint32_t m_EN_out8[2]; // enable 8' output masks
+ uint32_t m_EN_out4[2]; // enable 4' output masks
+ uint32_t m_EN_out2[2]; // enable 2' output masks
int m_noise_cnt;
int m_noise_step;
int m_noise_rng;
- int m_noise_clocks; /* number of the noise_rng (output) level changes */
+ int m_noise_clocks; // number of the noise_rng (output) level changes
unsigned int m_UpdateStep;
- /* rate tables */
+ // rate tables
double m_ar_tbl[8];
double m_dr_tbl[16];
- uint8_t m_control1;
- uint8_t m_control2;
+ uint8_t m_control1;
+ uint8_t m_control2;
- int m_gate; /* current state of the GATE output */
+ int m_gate; // current state of the GATE output
- int m_chip_clock; /* chip clock in Hz */
- int m_rate; /* sample rate in Hz */
+ int m_chip_clock; // chip clock in Hz
+ int m_rate; // sample rate in Hz
- double m_external_capacity[8]; /* in Farads, eg 0.39e-6 = 0.36 uF (microFarads) */
- devcb_write_line m_gate_handler_cb;/* callback called when the GATE output pin changes state */
+ double m_external_capacitance[8]; // in Farads, eg 0.39e-6 = 0.36 uF (microFarads)
+ devcb_write_line m_gate_handler_cb;// callback called when the GATE output pin changes state
void init_tables();
void init_voice(int i);
diff --git a/src/devices/sound/multipcm.cpp b/src/devices/sound/multipcm.cpp
index b3dd084580f..e73d290f648 100644
--- a/src/devices/sound/multipcm.cpp
+++ b/src/devices/sound/multipcm.cpp
@@ -22,9 +22,10 @@
* This sample format might be derived from the one used by the older YM7138 'GEW6' chip.
*
* The first 3 bytes are the offset into the file (big endian). (0, 1, 2).
- * Bit 23 is the sample format flag: 0 for 8-bit linear, 1 for 12-bit linear.
- * Bits 21 and 22 are used by the MU5 on some samples for as-yet unknown purposes.
- * The next 2 are the loop start point, in samples (big endian) (3, 4)
+ * Bit 23 is unknown.
+ * Bit 22 is the sample format flag: 0 for 8-bit linear, 1 for 12-bit linear.
+ * Bit 21 is used by the MU5 on some samples for as-yet unknown purposes. (YMW-258-F has 22 address pins.)
+ * The next 2 bytes are the loop start point, in samples (big endian) (3, 4)
* The next 2 are the 2's complement negation of of the total number of samples (big endian) (5, 6)
* The next byte is LFO freq + depth (copied to reg 6 ?) (7, 8)
* The next 3 are envelope params (Attack, Decay1 and 2, sustain level, release, Key Rate Scaling) (9, 10, 11)
@@ -36,34 +37,6 @@
#include "emu.h"
#include "multipcm.h"
-#include "wavwrite.h"
-
-ALLOW_SAVE_TYPE(multipcm_device::state_t); // allow save_item on a non-fundamental type
-
-/*******************************
- ENVELOPE SECTION
-*******************************/
-
-// Times are based on a 44100Hz timebase. It's adjusted to the actual sampling rate on startup
-
-const double multipcm_device::BASE_TIMES[64] = {
- 0, 0, 0, 0,
- 6222.95, 4978.37, 4148.66, 3556.01,
- 3111.47, 2489.21, 2074.33, 1778.00,
- 1555.74, 1244.63, 1037.19, 889.02,
- 777.87, 622.31, 518.59, 444.54,
- 388.93, 311.16, 259.32, 222.27,
- 194.47, 155.60, 129.66, 111.16,
- 97.23, 77.82, 64.85, 55.60,
- 48.62, 38.91, 32.43, 27.80,
- 24.31, 19.46, 16.24, 13.92,
- 12.15, 9.75, 8.12, 6.98,
- 6.08, 4.90, 4.08, 3.49,
- 3.04, 2.49, 2.13, 1.90,
- 1.72, 1.41, 1.18, 1.04,
- 0.91, 0.73, 0.59, 0.50,
- 0.45, 0.45, 0.45, 0.45
-};
const int32_t multipcm_device::VALUE_TO_CHANNEL[32] =
{
@@ -73,272 +46,28 @@ const int32_t multipcm_device::VALUE_TO_CHANNEL[32] =
21,22,23,24,25,26,27, -1,
};
-constexpr uint32_t multipcm_device::TL_SHIFT;
-constexpr uint32_t multipcm_device::EG_SHIFT;
-
-void multipcm_device::init_sample(sample_t *sample, uint32_t index)
+void multipcm_device::init_sample(sample_t &sample, uint32_t index)
{
uint32_t address = index * 12;
- sample->m_start = (read_byte(address) << 16) | (read_byte(address + 1) << 8) | read_byte(address + 2);
- sample->m_format = (sample->m_start>>20) & 0xfe;
- sample->m_start &= 0x3fffff;
- sample->m_loop = (read_byte(address + 3) << 8) | read_byte(address + 4);
- sample->m_end = 0xffff - ((read_byte(address + 5) << 8) | read_byte(address + 6));
- sample->m_attack_reg = (read_byte(address + 8) >> 4) & 0xf;
- sample->m_decay1_reg = read_byte(address + 8) & 0xf;
- sample->m_decay2_reg = read_byte(address + 9) & 0xf;
- sample->m_decay_level = (read_byte(address + 9) >> 4) & 0xf;
- sample->m_release_reg = read_byte(address + 10) & 0xf;
- sample->m_key_rate_scale = (read_byte(address + 10) >> 4) & 0xf;
- sample->m_lfo_vibrato_reg = read_byte(address + 7);
- sample->m_lfo_amplitude_reg = read_byte(address + 11) & 0xf;
-}
-
-void multipcm_device::retrigger_sample(slot_t &slot)
-{
- slot.m_offset = 0;
- slot.m_prev_sample = 0;
- slot.m_total_level = slot.m_dest_total_level << TL_SHIFT;
-
- envelope_generator_calc(slot);
- slot.m_envelope_gen.m_state = state_t::ATTACK;
- slot.m_envelope_gen.m_volume = 0;
-
-#if MULTIPCM_LOG_SAMPLES
- dump_sample(slot);
-#endif
-}
-
-int32_t multipcm_device::envelope_generator_update(slot_t &slot)
-{
- switch(slot.m_envelope_gen.m_state)
- {
- case state_t::ATTACK:
- slot.m_envelope_gen.m_volume += slot.m_envelope_gen.m_attack_rate;
- if (slot.m_envelope_gen.m_volume >= (0x3ff << EG_SHIFT))
- {
- slot.m_envelope_gen.m_state = state_t::DECAY1;
- if (slot.m_envelope_gen.m_decay1_rate >= (0x400 << EG_SHIFT)) //Skip DECAY1, go directly to DECAY2
- {
- slot.m_envelope_gen.m_state = state_t::DECAY2;
- }
- slot.m_envelope_gen.m_volume = 0x3ff << EG_SHIFT;
- }
- break;
- case state_t::DECAY1:
- slot.m_envelope_gen.m_volume -= slot.m_envelope_gen.m_decay1_rate;
- if (slot.m_envelope_gen.m_volume <= 0)
- {
- slot.m_envelope_gen.m_volume = 0;
- }
- if (slot.m_envelope_gen.m_volume >> EG_SHIFT <= (slot.m_envelope_gen.m_decay_level << 6))
- {
- slot.m_envelope_gen.m_state = state_t::DECAY2;
- }
- break;
- case state_t::DECAY2:
- slot.m_envelope_gen.m_volume -= slot.m_envelope_gen.m_decay2_rate;
- if (slot.m_envelope_gen.m_volume <= 0)
- {
- slot.m_envelope_gen.m_volume = 0;
- }
- break;
- case state_t::RELEASE:
- slot.m_envelope_gen.m_volume -= slot.m_envelope_gen.m_release_rate;
- if (slot.m_envelope_gen.m_volume <= 0)
- {
- slot.m_envelope_gen.m_volume = 0;
- slot.m_playing = false;
- }
- break;
- default:
- return 1 << TL_SHIFT;
- }
-
- return m_linear_to_exp_volume[slot.m_envelope_gen.m_volume >> EG_SHIFT];
-}
-
-uint32_t multipcm_device::get_rate(uint32_t *steps, uint32_t rate, uint32_t val)
-{
- int32_t r = 4 * val + rate;
- if (val == 0)
- {
- return steps[0];
- }
- if (val == 0xf)
- {
- return steps[0x3f];
- }
- if (r > 0x3f)
- {
- r = 0x3f;
- }
- return steps[r];
-}
-
-void multipcm_device::envelope_generator_calc(slot_t &slot)
-{
- int32_t octave = ((slot.m_regs[3] >> 4) - 1) & 0xf;
- if (octave & 8) {
- octave = octave - 16;
- }
-
- int32_t rate;
- if (slot.m_sample.m_key_rate_scale != 0xf)
- {
- rate = (octave + slot.m_sample.m_key_rate_scale) * 2 + ((slot.m_regs[3] >> 3) & 1);
- }
- else
- {
- rate = 0;
- }
-
- slot.m_envelope_gen.m_attack_rate = get_rate(m_attack_step.get(), rate, slot.m_sample.m_attack_reg);
- slot.m_envelope_gen.m_decay1_rate = get_rate(m_decay_release_step.get(), rate, slot.m_sample.m_decay1_reg);
- slot.m_envelope_gen.m_decay2_rate = get_rate(m_decay_release_step.get(), rate, slot.m_sample.m_decay2_reg);
- slot.m_envelope_gen.m_release_rate = get_rate(m_decay_release_step.get(), rate, slot.m_sample.m_release_reg);
- slot.m_envelope_gen.m_decay_level = 0xf - slot.m_sample.m_decay_level;
-
-}
-
-/*****************************
- LFO SECTION
-*****************************/
-
-constexpr uint32_t multipcm_device::LFO_SHIFT;
-
-const float multipcm_device::LFO_FREQ[8] = // In Hertz
-{
- 0.168f,
- 2.019f,
- 3.196f,
- 4.206f,
- 5.215f,
- 5.888f,
- 6.224f,
- 7.066f
-};
-
-const float multipcm_device::PHASE_SCALE_LIMIT[8] = // In Cents
-{
- 0.0f,
- 3.378f,
- 5.065f,
- 6.750f,
- 10.114f,
- 20.170f,
- 40.180f,
- 79.307f
-};
-
-const float multipcm_device::AMPLITUDE_SCALE_LIMIT[8] = // In Decibels
-{
- 0.0f,
- 0.4f,
- 0.8f,
- 1.5f,
- 3.0f,
- 6.0f,
- 12.0f,
- 24.0f
-};
-
-void multipcm_device::lfo_init()
-{
- m_pitch_table = make_unique_clear<int32_t[]>(256);
- m_amplitude_table = make_unique_clear<int32_t[]>(256);
- for (int32_t i = 0; i < 256; ++i)
- {
- if (i < 64)
- {
- m_pitch_table[i] = i * 2 + 128;
- }
- else if (i < 128)
- {
- m_pitch_table[i] = 383 - i * 2;
- }
- else if (i < 192)
- {
- m_pitch_table[i] = 384 - i * 2;
- }
- else
- {
- m_pitch_table[i] = i * 2 - 383;
- }
-
- if (i < 128)
- {
- m_amplitude_table[i] = 255 - (i * 2);
- }
- else
- {
- m_amplitude_table[i] = (i * 2) - 256;
- }
- }
-
- for (int32_t table = 0; table < 8; ++table)
- {
- float limit = PHASE_SCALE_LIMIT[table];
- m_pitch_scale_tables[table] = make_unique_clear<int32_t[]>(256);
- for(int32_t i = -128; i < 128; ++i)
- {
- const float value = (limit * (float)i) / 128.0f;
- const float converted = powf(2.0f, value / 1200.0f);
- m_pitch_scale_tables[table][i + 128] = value_to_fixed(LFO_SHIFT, converted);
- }
-
- limit = -AMPLITUDE_SCALE_LIMIT[table];
- m_amplitude_scale_tables[table] = make_unique_clear<int32_t[]>(256);
- for(int32_t i = 0; i < 256; ++i)
- {
- const float value = (limit * (float)i) / 256.0f;
- const float converted = powf(10.0f, value / 20.0f);
- m_amplitude_scale_tables[table][i] = value_to_fixed(LFO_SHIFT, converted);
- }
- }
-}
-
-uint32_t multipcm_device::value_to_fixed(const uint32_t bits, const float value)
-{
- const float float_shift = float(1 << bits);
- return uint32_t(float_shift * value);
-}
-
-int32_t multipcm_device::pitch_lfo_step(lfo_t &lfo)
-{
- lfo.m_phase += lfo.m_phase_step;
- int32_t p = lfo.m_table[(lfo.m_phase >> LFO_SHIFT) & 0xff];
- p = lfo.m_scale[p];
- return p << (TL_SHIFT - LFO_SHIFT);
-}
-
-int32_t multipcm_device::amplitude_lfo_step(lfo_t &lfo)
-{
- lfo.m_phase += lfo.m_phase_step;
- int32_t p = lfo.m_table[(lfo.m_phase >> LFO_SHIFT) & 0xff];
- p = lfo.m_scale[p];
- return p << (TL_SHIFT - LFO_SHIFT);
-}
-
-void multipcm_device::lfo_compute_step(lfo_t &lfo, uint32_t lfo_frequency, uint32_t lfo_scale, int32_t amplitude_lfo)
-{
- float step = (float)LFO_FREQ[lfo_frequency] * 256.0f / (float)m_rate;
- lfo.m_phase_step = uint32_t(float(1 << LFO_SHIFT) * step);
- if (amplitude_lfo)
- {
- lfo.m_table = m_amplitude_table.get();
- lfo.m_scale = m_amplitude_scale_tables[lfo_scale].get();
- }
- else
- {
- lfo.m_table = m_pitch_table.get();
- lfo.m_scale = m_pitch_scale_tables[lfo_scale].get();
- }
+ sample.m_start = (read_byte(address) << 16) | (read_byte(address + 1) << 8) | read_byte(address + 2);
+ sample.m_format = (sample.m_start>>20) & 0xfe;
+ sample.m_start &= 0x3fffff;
+ sample.m_loop = (read_byte(address + 3) << 8) | read_byte(address + 4);
+ sample.m_end = 0x10000 - ((read_byte(address + 5) << 8) | read_byte(address + 6));
+ sample.m_attack_reg = (read_byte(address + 8) >> 4) & 0xf;
+ sample.m_decay1_reg = read_byte(address + 8) & 0xf;
+ sample.m_decay2_reg = read_byte(address + 9) & 0xf;
+ sample.m_decay_level = (read_byte(address + 9) >> 4) & 0xf;
+ sample.m_release_reg = read_byte(address + 10) & 0xf;
+ sample.m_key_rate_scale = (read_byte(address + 10) >> 4) & 0xf;
+ sample.m_lfo_vibrato_reg = read_byte(address + 7);
+ sample.m_lfo_amplitude_reg = read_byte(address + 11) & 0xf;
}
void multipcm_device::write_slot(slot_t &slot, int32_t reg, uint8_t data)
{
+ m_stream->update();
slot.m_regs[reg] = data;
switch(reg)
@@ -350,13 +79,10 @@ void multipcm_device::write_slot(slot_t &slot, int32_t reg, uint8_t data)
case 1: // Sample
{
// according to YMF278 sample write causes some base params written to the regs (envelope+lfos)
- init_sample(&slot.m_sample, slot.m_regs[1] | ((slot.m_regs[2] & 1) << 8));
+ init_sample(slot.m_sample, slot.m_regs[1] | ((slot.m_regs[2] & 1) << 8));
write_slot(slot, 6, slot.m_sample.m_lfo_vibrato_reg);
write_slot(slot, 7, slot.m_sample.m_lfo_amplitude_reg);
- slot.m_base = slot.m_sample.m_start;
- slot.m_format = slot.m_sample.m_format;
-
// retrigger if key is on
if (slot.m_playing)
retrigger_sample(slot);
@@ -366,18 +92,9 @@ void multipcm_device::write_slot(slot_t &slot, int32_t reg, uint8_t data)
case 2: // Pitch
case 3:
{
- uint32_t oct = ((slot.m_regs[3] >> 4) - 1) & 0xf;
- uint32_t pitch = ((slot.m_regs[3] & 0xf) << 6) | (slot.m_regs[2] >> 2);
- pitch = m_freq_step_table[pitch];
- if (oct & 0x8)
- {
- pitch >>= (16 - oct);
- }
- else
- {
- pitch <<= oct;
- }
- slot.m_step = pitch / m_rate;
+ slot.m_octave = slot.m_regs[3] >> 4;
+ slot.m_pitch = ((slot.m_regs[3] & 0xf) << 6) | (slot.m_regs[2] >> 2);
+ update_step(slot);
}
break;
case 4: // KeyOn/Off
@@ -420,17 +137,14 @@ void multipcm_device::write_slot(slot_t &slot, int32_t reg, uint8_t data)
}
break;
case 6: // LFO frequency + Pitch LFO
- if (data)
- {
- lfo_compute_step(slot.m_pitch_lfo, (slot.m_regs[6] >> 3) & 7, slot.m_regs[6] & 7, 0);
- lfo_compute_step(slot.m_amplitude_lfo, (slot.m_regs[6] >> 3) & 7, slot.m_regs[7] & 7, 1);
- }
- break;
case 7: // Amplitude LFO
+ slot.m_lfo_frequency = (slot.m_regs[6] >> 3) & 7;
+ slot.m_vibrato = slot.m_regs[6] & 7;
+ slot.m_tremolo = slot.m_regs[7] & 7;
if (data)
{
- lfo_compute_step(slot.m_pitch_lfo, (slot.m_regs[6] >> 3) & 7, slot.m_regs[6] & 7, 0);
- lfo_compute_step(slot.m_amplitude_lfo, (slot.m_regs[6] >> 3) & 7, slot.m_regs[7] & 7, 1);
+ lfo_compute_step(slot.m_pitch_lfo, slot.m_lfo_frequency, slot.m_vibrato, 0);
+ lfo_compute_step(slot.m_amplitude_lfo, slot.m_lfo_frequency, slot.m_tremolo, 1);
}
break;
}
@@ -464,324 +178,8 @@ void multipcm_device::write(offs_t offset, uint8_t data)
DEFINE_DEVICE_TYPE(MULTIPCM, multipcm_device, "ymw258f", "Yamaha YMW-258-F")
multipcm_device::multipcm_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
- device_t(mconfig, MULTIPCM, tag, owner, clock),
- device_sound_interface(mconfig, *this),
- device_rom_interface(mconfig, *this),
- m_stream(nullptr),
- m_slots(nullptr),
+ gew_pcm_device(mconfig, MULTIPCM, tag, owner, clock, 28, 224),
m_cur_slot(0),
- m_address(0),
- m_rate(0),
- m_attack_step(nullptr),
- m_decay_release_step(nullptr),
- m_freq_step_table(nullptr),
- m_left_pan_table(nullptr),
- m_right_pan_table(nullptr),
- m_linear_to_exp_volume(nullptr),
- m_total_level_steps(nullptr)
-{
-}
-
-
-//-------------------------------------------------
-// device_start - device-specific startup
-//-------------------------------------------------
-
-void multipcm_device::device_start()
-{
- const float clock_divider = 224.0f;
- m_rate = (float)clock() / clock_divider;
-
- m_stream = stream_alloc(0, 2, m_rate);
-
- // Volume + pan table
- m_left_pan_table = make_unique_clear<int32_t[]>(0x800);
- m_right_pan_table = make_unique_clear<int32_t[]>(0x800);
- for (int32_t level = 0; level < 0x80; ++level)
- {
- const float vol_db = (float)level * (-24.0f) / 64.0f;
- const float total_level = powf(10.0f, vol_db / 20.0f) / 4.0f;
-
- for (int32_t pan = 0; pan < 0x10; ++pan)
- {
- float pan_left, pan_right;
- if (pan == 0x8)
- {
- pan_left = 0.0;
- pan_right = 0.0;
- }
- else if (pan == 0x0)
- {
- pan_left = 1.0;
- pan_right = 1.0;
- }
- else if (pan & 0x8)
- {
- pan_left = 1.0;
-
- const int32_t inverted_pan = 0x10 - pan;
- const float pan_vol_db = (float)inverted_pan * (-12.0f) / 4.0f;
-
- pan_right = pow(10.0f, pan_vol_db / 20.0f);
-
- if ((inverted_pan & 0x7) == 7)
- {
- pan_right = 0.0;
- }
- }
- else
- {
- pan_right = 1.0;
-
- const float pan_vol_db = (float)pan * (-12.0f) / 4.0f;
-
- pan_left = pow(10.0f, pan_vol_db / 20.0f);
-
- if ((pan & 0x7) == 7)
- {
- pan_left = 0.0;
- }
- }
-
- m_left_pan_table[(pan << 7) | level] = value_to_fixed(TL_SHIFT, pan_left * total_level);
- m_right_pan_table[(pan << 7) | level] = value_to_fixed(TL_SHIFT, pan_right * total_level);
- }
- }
-
- // Pitch steps
- m_freq_step_table = make_unique_clear<uint32_t[]>(0x400);
- for (int32_t i = 0; i < 0x400; ++i)
- {
- const float fcent = m_rate * (1024.0f + (float)i) / 1024.0f;
- m_freq_step_table[i] = value_to_fixed(TL_SHIFT, fcent);
- }
-
- // Envelope steps
- m_attack_step = make_unique_clear<uint32_t[]>(0x40);
- m_decay_release_step = make_unique_clear<uint32_t[]>(0x40);
- const double attack_rate_to_decay_rate = 14.32833;
- for (int32_t i = 4; i < 0x40; ++i)
- {
- // Times are based on 44100Hz clock, adjust to real chip clock
- m_attack_step[i] = (float)(0x400 << EG_SHIFT) / (float)(BASE_TIMES[i] * 44100.0 / 1000.0);
- m_decay_release_step[i] = (float)(0x400 << EG_SHIFT) / (float)(BASE_TIMES[i] * attack_rate_to_decay_rate * 44100.0 / 1000.0);
- }
- m_attack_step[0] = m_attack_step[1] = m_attack_step[2] = m_attack_step[3] = 0;
- m_attack_step[0x3f] = 0x400 << EG_SHIFT;
- m_decay_release_step[0] = m_decay_release_step[1] = m_decay_release_step[2] = m_decay_release_step[3] = 0;
-
- // Total level interpolation steps
- m_total_level_steps = make_unique_clear<int32_t[]>(2);
- m_total_level_steps[0] = -(float)(0x80 << TL_SHIFT) / (78.2f * 44100.0f / 1000.0f); // lower
- m_total_level_steps[1] = (float)(0x80 << TL_SHIFT) / (78.2f * 2 * 44100.0f / 1000.0f); // raise
-
- // build the linear->exponential ramps
- m_linear_to_exp_volume = make_unique_clear<int32_t[]>(0x400);
- for(int32_t i = 0; i < 0x400; ++i)
- {
- const float db = -(96.0f - (96.0f * (float)i / (float)0x400));
- const float exp_volume = powf(10.0f, db / 20.0f);
- m_linear_to_exp_volume[i] = value_to_fixed(TL_SHIFT, exp_volume);
- }
-
- save_item(NAME(m_cur_slot));
- save_item(NAME(m_address));
-
- // Slots
- m_slots = std::make_unique<slot_t []>(28);
-
- save_pointer(STRUCT_MEMBER(m_slots, m_regs), 28);
- save_pointer(STRUCT_MEMBER(m_slots, m_playing), 28);
- save_pointer(STRUCT_MEMBER(m_slots, m_base), 28);
- save_pointer(STRUCT_MEMBER(m_slots, m_offset), 28);
- save_pointer(STRUCT_MEMBER(m_slots, m_step), 28);
- save_pointer(STRUCT_MEMBER(m_slots, m_pan), 28);
- save_pointer(STRUCT_MEMBER(m_slots, m_total_level), 28);
- save_pointer(STRUCT_MEMBER(m_slots, m_dest_total_level), 28);
- save_pointer(STRUCT_MEMBER(m_slots, m_total_level_step), 28);
- save_pointer(STRUCT_MEMBER(m_slots, m_prev_sample), 28);
- save_pointer(STRUCT_MEMBER(m_slots, m_format), 28);
-
- for (int32_t slot = 0; slot < 28; ++slot)
- {
- m_slots[slot].m_playing = false;
-
- save_item(NAME(m_slots[slot].m_envelope_gen.m_volume), slot);
- save_item(NAME(m_slots[slot].m_envelope_gen.m_state), slot);
- save_item(NAME(m_slots[slot].m_envelope_gen.step), slot);
- save_item(NAME(m_slots[slot].m_envelope_gen.m_attack_rate), slot);
- save_item(NAME(m_slots[slot].m_envelope_gen.m_decay1_rate), slot);
- save_item(NAME(m_slots[slot].m_envelope_gen.m_decay2_rate), slot);
- save_item(NAME(m_slots[slot].m_envelope_gen.m_release_rate), slot);
- save_item(NAME(m_slots[slot].m_envelope_gen.m_decay_level), slot);
- save_item(NAME(m_slots[slot].m_pitch_lfo.m_phase), slot);
- save_item(NAME(m_slots[slot].m_pitch_lfo.m_phase_step), slot);
- save_item(NAME(m_slots[slot].m_amplitude_lfo.m_phase), slot);
- save_item(NAME(m_slots[slot].m_amplitude_lfo.m_phase_step), slot);
- }
-
- lfo_init();
-}
-
-//-------------------------------------------------
-// device_clock_changed - called if the clock
-// changes
-//-------------------------------------------------
-
-void multipcm_device::device_clock_changed()
+ m_address(0)
{
- const float clock_divider = 224.0f;
- m_rate = (float)clock() / clock_divider;
- m_stream->set_sample_rate(m_rate);
-
- for (int32_t i = 0; i < 0x400; ++i)
- {
- const float fcent = m_rate * (1024.0f + (float)i) / 1024.0f;
- m_freq_step_table[i] = value_to_fixed(TL_SHIFT, fcent);
- }
-}
-
-//-----------------------------------------------------
-// dump_sample - dump current sample to WAV file
-//-----------------------------------------------------
-
-#if MULTIPCM_LOG_SAMPLES
-void multipcm_device::dump_sample(slot_t &slot)
-{
- if (m_logged_map[slot.m_base])
- return;
-
- m_logged_map[slot.m_base] = true;
-
- char filebuf[256];
- snprintf(filebuf, 256, "multipcm%08x.wav", slot.m_base);
- wav_file *file = wav_open(filebuf, m_stream->sample_rate(), 1);
- if (file == nullptr)
- return;
-
- uint32_t offset = slot.m_offset;
- bool done = false;
- while (!done)
- {
- int16_t sample = (int16_t) (read_byte(slot.m_base + (offset >> TL_SHIFT)) << 8);
- wav_add_data_16(file, &sample, 1);
-
- offset += 1 << TL_SHIFT;
- if (offset >= (slot.m_sample.m_end << TL_SHIFT))
- {
- done = true;
- }
- }
-
- wav_close(file);
-}
-#endif
-
-//-------------------------------------------------
-// sound_stream_update - handle a stream update
-//-------------------------------------------------
-
-void multipcm_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
-{
- for (int32_t i = 0; i < outputs[0].samples(); ++i)
- {
- int32_t smpl = 0;
- int32_t smpr = 0;
- for (int32_t sl = 0; sl < 28; ++sl)
- {
- slot_t &slot = m_slots[sl];
- if (slot.m_playing)
- {
- uint32_t vol = (slot.m_total_level >> TL_SHIFT) | (slot.m_pan << 7);
- uint32_t spos = slot.m_offset >> TL_SHIFT;
- uint32_t step = slot.m_step;
- int32_t csample = 0;
- int32_t fpart = slot.m_offset & ((1 << TL_SHIFT) - 1);
-
- if (slot.m_format & 8) // 12-bit linear
- {
- offs_t adr = slot.m_base + (spos >> 2) * 6;
- switch (spos & 3)
- {
- case 0:
- { // ab.c .... ....
- s16 w0 = read_byte(adr) << 8 | ((read_byte(adr + 1) & 0xf) << 4);
- csample = w0;
- break;
- }
- case 1:
- { // ..C. AB.. ....
- s16 w0 = (read_byte(adr + 2) << 8) | (read_byte(adr + 1) & 0xf0);
- csample = w0;
- break;
- }
- case 2:
- { // .... ..ab .c..
- s16 w0 = read_byte(adr + 3) << 8 | ((read_byte(adr + 4) & 0xf) << 4);
- csample = w0;
- break;
- }
- case 3:
- { // .... .... C.AB
- s16 w0 = (read_byte(adr + 5) << 8) | (read_byte(adr + 4) & 0xf0);
- csample = w0;
- break;
- }
- }
- }
- else
- {
- csample = (int16_t)(read_byte(slot.m_base + spos) << 8);
- }
-
- int32_t sample = (csample * fpart + slot.m_prev_sample * ((1 << TL_SHIFT) - fpart)) >> TL_SHIFT;
-
- if (slot.m_regs[6] & 7) // Vibrato enabled
- {
- step = step * pitch_lfo_step(slot.m_pitch_lfo);
- step >>= TL_SHIFT;
- }
-
- slot.m_offset += step;
- if (slot.m_offset >= (slot.m_sample.m_end << TL_SHIFT))
- {
- slot.m_offset = slot.m_sample.m_loop << TL_SHIFT;
- }
-
- if (spos ^ (slot.m_offset >> TL_SHIFT))
- {
- slot.m_prev_sample = csample;
- }
-
- if ((slot.m_total_level >> TL_SHIFT) != slot.m_dest_total_level)
- {
- slot.m_total_level += slot.m_total_level_step;
- }
-
- if (slot.m_regs[7] & 7) // Tremolo enabled
- {
- sample = sample * amplitude_lfo_step(slot.m_amplitude_lfo);
- sample >>= TL_SHIFT;
- }
-
- sample = (sample * envelope_generator_update(slot)) >> 10;
-
- smpl += (m_left_pan_table[vol] * sample) >> TL_SHIFT;
- smpr += (m_right_pan_table[vol] * sample) >> TL_SHIFT;
- }
- }
-
- outputs[0].put_int_clamp(i, smpl, 32768);
- outputs[1].put_int_clamp(i, smpr, 32768);
- }
-}
-
-
-//-------------------------------------------------
-// rom_bank_updated - the rom bank has changed
-//-------------------------------------------------
-
-void multipcm_device::rom_bank_updated()
-{
- m_stream->update();
}
diff --git a/src/devices/sound/multipcm.h b/src/devices/sound/multipcm.h
index 27f5f6d5de0..c6b5ed8d4b1 100644
--- a/src/devices/sound/multipcm.h
+++ b/src/devices/sound/multipcm.h
@@ -5,17 +5,9 @@
#pragma once
-#include "dirom.h"
+#include "gew.h"
-#define MULTIPCM_LOG_SAMPLES 0
-
-#if MULTIPCM_LOG_SAMPLES
-#include <map>
-#endif
-
-class multipcm_device : public device_t,
- public device_sound_interface,
- public device_rom_interface<24>
+class multipcm_device : public gew_pcm_device
{
public:
multipcm_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
@@ -23,139 +15,16 @@ public:
void write(offs_t offset, uint8_t data);
uint8_t read();
-protected:
- // device-level overrides
- virtual void device_start() override;
- virtual void device_clock_changed() override;
-
- // sound stream update overrides
- virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
-
- // device_rom_interface overrides
- virtual void rom_bank_updated() override;
-
private:
- struct sample_t
- {
- uint32_t m_start = 0;
- uint32_t m_loop = 0;
- uint32_t m_end = 0;
- uint8_t m_attack_reg = 0;
- uint8_t m_decay1_reg = 0;
- uint8_t m_decay2_reg = 0;
- uint8_t m_decay_level = 0;
- uint8_t m_release_reg = 0;
- uint8_t m_key_rate_scale = 0;
- uint8_t m_lfo_vibrato_reg = 0;
- uint8_t m_lfo_amplitude_reg = 0;
- uint8_t m_format = 0;
- };
-
- enum class state_t : u8
- {
- ATTACK,
- DECAY1,
- DECAY2,
- RELEASE
- };
-
- struct envelope_gen_t
- {
- int32_t m_volume = 0;
- state_t m_state = state_t::ATTACK;
- int32_t step = 0;
- //step vals
- int32_t m_attack_rate = 0; // Attack
- int32_t m_decay1_rate = 0; // Decay1
- int32_t m_decay2_rate = 0; // Decay2
- int32_t m_release_rate = 0; // Release
- int32_t m_decay_level = 0; // Decay level
- };
-
- struct lfo_t
- {
- uint16_t m_phase = 0;
- uint32_t m_phase_step = 0;
- int32_t *m_table = nullptr;
- int32_t *m_scale = nullptr;
- };
-
- struct slot_t
- {
- uint8_t m_regs[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
- bool m_playing = false;
- sample_t m_sample;
- uint32_t m_base = 0;
- uint32_t m_offset = 0;
- uint32_t m_step = 0;
- uint32_t m_pan = 0;
- uint32_t m_total_level = 0;
- uint32_t m_dest_total_level = 0;
- int32_t m_total_level_step = 0;
- int32_t m_prev_sample = 0;
- envelope_gen_t m_envelope_gen;
- lfo_t m_pitch_lfo; // Pitch lfo
- lfo_t m_amplitude_lfo; // AM lfo
- uint8_t m_format;
- };
-
// internal state
- sound_stream *m_stream;
- std::unique_ptr<slot_t[]> m_slots;
uint32_t m_cur_slot;
uint32_t m_address;
- float m_rate;
-
- std::unique_ptr<uint32_t[]> m_attack_step;
- std::unique_ptr<uint32_t[]> m_decay_release_step; // Envelope step tables
- std::unique_ptr<uint32_t[]> m_freq_step_table; // Frequency step table
-
- std::unique_ptr<int32_t[]> m_left_pan_table;
- std::unique_ptr<int32_t[]> m_right_pan_table;
- std::unique_ptr<int32_t[]> m_linear_to_exp_volume;
- std::unique_ptr<int32_t[]> m_total_level_steps;
-
- std::unique_ptr<int32_t[]> m_pitch_table;
- std::unique_ptr<int32_t[]> m_pitch_scale_tables[8];
- std::unique_ptr<int32_t[]> m_amplitude_table;
- std::unique_ptr<int32_t[]> m_amplitude_scale_tables[8];
-
- uint32_t value_to_fixed(const uint32_t bits, const float value);
-
- void init_sample(sample_t *sample, uint32_t index);
- void retrigger_sample(slot_t &slot);
-
- // Internal LFO functions
- void lfo_init();
- void lfo_compute_step(lfo_t &lfo, uint32_t lfo_frequency, uint32_t LFOS, int32_t amplitude_lfo);
- int32_t pitch_lfo_step(lfo_t &lfo);
- int32_t amplitude_lfo_step(lfo_t &lfo);
-
- // Internal envelope functions
- int32_t envelope_generator_update(slot_t &slot);
- void envelope_generator_calc(slot_t &slot);
- uint32_t get_rate(uint32_t *steps, uint32_t rate, uint32_t val);
-
- void write_slot(slot_t &slot, int32_t reg, uint8_t data);
-
- stream_buffer::sample_t convert_to_stream_sample(int32_t value);
-
-#if MULTIPCM_LOG_SAMPLES
- void dump_sample(slot_t &slot);
- std::map<uint32_t, bool> m_logged_map;
-#endif
-
- static constexpr uint32_t TL_SHIFT = 12;
static const int32_t VALUE_TO_CHANNEL[32];
- static constexpr uint32_t EG_SHIFT = 16;
- static const double BASE_TIMES[64];
+ void init_sample(sample_t &sample, uint32_t index);
- static constexpr uint32_t LFO_SHIFT = 8;
- static const float LFO_FREQ[8];
- static const float PHASE_SCALE_LIMIT[8];
- static const float AMPLITUDE_SCALE_LIMIT[8];
+ void write_slot(slot_t &slot, int32_t reg, uint8_t data);
};
DECLARE_DEVICE_TYPE(MULTIPCM, multipcm_device)
diff --git a/src/devices/sound/namco_163.cpp b/src/devices/sound/namco_163.cpp
index c26a1f1c638..3735e3147df 100644
--- a/src/devices/sound/namco_163.cpp
+++ b/src/devices/sound/namco_163.cpp
@@ -63,7 +63,7 @@ inline s8 namco_163_sound_device::get_sample(u16 addr)
}
-WRITE_LINE_MEMBER(namco_163_sound_device::disable_w)
+void namco_163_sound_device::disable_w(int state)
{
m_disable = state;
}
diff --git a/src/devices/sound/namco_163.h b/src/devices/sound/namco_163.h
index f3a7428300d..541f0e053b7 100644
--- a/src/devices/sound/namco_163.h
+++ b/src/devices/sound/namco_163.h
@@ -12,7 +12,7 @@ class namco_163_sound_device : public device_t,
public:
namco_163_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
- DECLARE_WRITE_LINE_MEMBER(disable_w);
+ void disable_w(int state);
void addr_w(u8 data);
void data_w(u8 data);
diff --git a/src/devices/sound/nes_apu.cpp b/src/devices/sound/nes_apu.cpp
index a7e8a3066f7..86c8a8c8934 100644
--- a/src/devices/sound/nes_apu.cpp
+++ b/src/devices/sound/nes_apu.cpp
@@ -4,16 +4,12 @@
MAME/MESS NES APU CORE
- Based on the Nofrendo/Nosefart NES N2A03 sound emulation core written by
+ Based on the Nofrendo/Nosefart NES RP2A03 sound emulation core written by
Matthew Conte (matt@conte.com) and redesigned for use in MAME/MESS by
Who Wants to Know? (wwtk@mail.com)
This core is written with the advise and consent of Matthew Conte and is
- released under the GNU Public License. This core is freely available for
- use in any freeware project, subject to the following terms:
-
- Any modifications to this code must be duly noted in the source and
- approved by Matthew Conte and myself prior to public submission.
+ released under the GNU Public License.
timing notes:
master = 21477270
@@ -45,7 +41,8 @@
#include "emu.h"
#include "nes_apu.h"
-DEFINE_DEVICE_TYPE(NES_APU, nesapu_device, "nesapu", "N2A03 APU")
+DEFINE_DEVICE_TYPE(NES_APU, nesapu_device, "nesapu", "RP2A0X APU")
+DEFINE_DEVICE_TYPE(APU_2A03, apu2a03_device, "apu2a03", "RP2A03 APU")
nesapu_device::nesapu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock)
: device_t(mconfig, type, tag, owner, clock)
@@ -54,7 +51,8 @@ nesapu_device::nesapu_device(const machine_config &mconfig, device_type type, co
, m_samps_per_sync(0)
, m_stream(nullptr)
, m_irq_handler(*this)
- , m_mem_read_cb(*this)
+ , m_mem_read_cb(*this, 0x00)
+ , m_frame_timer(nullptr)
{
}
@@ -63,9 +61,19 @@ nesapu_device::nesapu_device(const machine_config& mconfig, const char* tag, dev
{
}
+apu2a03_device::apu2a03_device(const machine_config& mconfig, const char* tag, device_t* owner, u32 clock)
+ : nesapu_device(mconfig, APU_2A03, tag, owner, clock)
+{
+}
+
+
void nesapu_device::device_reset()
{
write(0x15, 0x00);
+ m_APU.tri.adder = 0;
+
+ if (m_APU.frame_irq_enabled)
+ m_frame_timer->adjust(m_frame_period, 0, m_frame_period);
}
void nesapu_device::device_clock_changed()
@@ -76,7 +84,7 @@ void nesapu_device::device_clock_changed()
void nesapu_device::calculate_rates()
{
- m_samps_per_sync = 89490 / 12; // Is there a different PAL value?
+ m_samps_per_sync = m_frame_clocks / 4; // FIXME: tables are 4-step mode ONLY
// initialize sample times in terms of vsyncs
for (int i = 0; i < SYNCS_MAX1; i++)
@@ -102,9 +110,11 @@ void nesapu_device::calculate_rates()
void nesapu_device::device_start()
{
- // resolve callbacks
- m_irq_handler.resolve_safe();
- m_mem_read_cb.resolve_safe(0x00);
+ m_frame_timer = timer_alloc(FUNC(nesapu_device::frame_timer_cb), this);
+ m_frame_clocks = m_is_pal ? 33254 : 29830;
+ m_frame_period = clocks_to_attotime(m_frame_clocks);
+ m_APU.step_mode = 4;
+ m_APU.frame_irq_enabled = true;
calculate_rates();
@@ -177,7 +187,7 @@ void nesapu_device::device_start()
save_item(NAME(m_APU.tri.output));
save_item(NAME(m_APU.noi.regs));
- save_item(NAME(m_APU.noi.seed));
+ save_item(NAME(m_APU.noi.lfsr));
save_item(NAME(m_APU.noi.vbl_length));
save_item(NAME(m_APU.noi.phaseacc));
save_item(NAME(m_APU.noi.env_phase));
@@ -197,8 +207,23 @@ void nesapu_device::device_start()
save_item(NAME(m_APU.dpcm.output));
save_item(NAME(m_APU.step_mode));
+ save_item(NAME(m_APU.frame_irq_enabled));
+ save_item(NAME(m_APU.frame_irq_occurred));
+}
+
+
+TIMER_CALLBACK_MEMBER(nesapu_device::frame_timer_cb)
+{
+ m_stream->update();
+
+ if (m_APU.step_mode == 4 && m_APU.frame_irq_enabled)
+ {
+ m_APU.frame_irq_occurred = true;
+ m_irq_handler(true);
+ }
}
+
/* TODO: sound channels should *ALL* have DC volume decay */
/* OUTPUT SQUARE WAVE SAMPLE (VALUES FROM 0 to +15) */
@@ -385,10 +410,10 @@ void nesapu_device::apu_noise(apu_t::noise_t *chan)
while (chan->phaseacc < 0)
{
chan->phaseacc += freq;
- chan->seed = (chan->seed >> 1) | ((BIT(chan->seed, 0) ^ BIT(chan->seed, (chan->regs[2] & 0x80) ? 6 : 1)) << 14);
+ update_lfsr(*chan);
}
- if (BIT(chan->seed, 0)) /* make it silence */
+ if (BIT(chan->lfsr, 0)) /* silence channel */
{
chan->output = 0;
return;
@@ -400,13 +425,24 @@ void nesapu_device::apu_noise(apu_t::noise_t *chan)
chan->output = 0x0f - chan->env_vol;
}
+void nesapu_device::update_lfsr(apu_t::noise_t &chan)
+{
+ chan.lfsr |= (BIT(chan.lfsr, 0) ^ BIT(chan.lfsr, (chan.regs[2] & 0x80) ? 6 : 1)) << 15;
+ chan.lfsr >>= 1;
+}
+
+void apu2a03_device::update_lfsr(apu_t::noise_t &chan)
+{
+ chan.lfsr |= (BIT(chan.lfsr, 0) ^ BIT(chan.lfsr, 1)) << 15;
+ chan.lfsr >>= 1;
+}
+
/* RESET DPCM PARAMETERS */
static inline void apu_dpcmreset(apu_t::dpcm_t *chan)
{
chan->address = 0xc000 + u16(chan->regs[2] << 6);
chan->length = u16(chan->regs[3] << 4) + 1;
chan->bits_left = chan->length << 3;
- chan->irq_occurred = false;
chan->enabled = true; /* Fixed * Proper DPCM channel ENABLE/DISABLE flag behaviour*/
}
@@ -447,7 +483,6 @@ void nesapu_device::apu_dpcm(apu_t::dpcm_t *chan)
}
}
-
chan->bits_left--;
bit_pos = 7 - (chan->bits_left & 7);
if (7 == bit_pos)
@@ -588,9 +623,11 @@ void nesapu_device::write(offs_t offset, u8 value)
/* DMC */
case apu_t::WRE0:
m_APU.dpcm.regs[0] = value;
- if (!(value & 0x80)) {
- m_irq_handler(false);
+ if (!(value & 0x80))
+ {
m_APU.dpcm.irq_occurred = false;
+ if (!m_APU.frame_irq_occurred)
+ m_irq_handler(false);
}
break;
@@ -609,10 +646,24 @@ void nesapu_device::write(offs_t offset, u8 value)
break;
case apu_t::IRQCTRL:
- if(value & 0x80)
+ if (value & 0x80)
m_APU.step_mode = 5;
else
m_APU.step_mode = 4;
+
+ m_APU.frame_irq_enabled = !BIT(value, 6);
+ if (m_APU.frame_irq_enabled)
+ {
+ m_frame_timer->adjust(m_frame_period, 0, m_frame_period);
+ }
+ else
+ {
+ m_APU.frame_irq_occurred = false;
+ if (!m_APU.dpcm.irq_occurred)
+ m_irq_handler(false);
+ m_frame_timer->reset();
+ }
+
break;
case apu_t::SMASK:
@@ -663,8 +714,9 @@ void nesapu_device::write(offs_t offset, u8 value)
else
m_APU.dpcm.enabled = false;
- //m_irq_handler(false);
m_APU.dpcm.irq_occurred = false;
+ if (!m_APU.frame_irq_occurred)
+ m_irq_handler(false);
break;
default:
@@ -675,34 +727,38 @@ logerror("invalid apu write: $%02X at $%04X\n", value, offset);
}
}
-/* READ VALUES FROM REGISTERS */
-u8 nesapu_device::read(offs_t offset)
+// Read status register at $4015
+u8 nesapu_device::status_r()
{
- if (offset == 0x15) /*FIXED* Address $4015 has different behaviour*/
- {
- int readval = 0;
- if (m_APU.squ[0].vbl_length > 0)
- readval |= 0x01;
+ m_stream->update();
- if (m_APU.squ[1].vbl_length > 0)
- readval |= 0x02;
+ u8 readval = 0;
+ if (m_APU.squ[0].vbl_length > 0)
+ readval |= 0x01;
- if (m_APU.tri.vbl_length > 0)
- readval |= 0x04;
+ if (m_APU.squ[1].vbl_length > 0)
+ readval |= 0x02;
- if (m_APU.noi.vbl_length > 0)
- readval |= 0x08;
+ if (m_APU.tri.vbl_length > 0)
+ readval |= 0x04;
- if (m_APU.dpcm.enabled)
- readval |= 0x10;
+ if (m_APU.noi.vbl_length > 0)
+ readval |= 0x08;
- if (m_APU.dpcm.irq_occurred)
- readval |= 0x80;
+ if (m_APU.dpcm.enabled)
+ readval |= 0x10;
- return readval;
- }
- else
- return 0xff; // FIXME: this should be open bus?
+ if (m_APU.frame_irq_occurred)
+ readval |= 0x40;
+
+ if (m_APU.dpcm.irq_occurred)
+ readval |= 0x80;
+
+ m_APU.frame_irq_occurred = false;
+ if (!m_APU.dpcm.irq_occurred)
+ m_irq_handler(false);
+
+ return readval;
}
diff --git a/src/devices/sound/nes_apu.h b/src/devices/sound/nes_apu.h
index bb694b79ffa..2ebfcf3f84b 100644
--- a/src/devices/sound/nes_apu.h
+++ b/src/devices/sound/nes_apu.h
@@ -4,16 +4,12 @@
MAME/MESS NES APU CORE
- Based on the Nofrendo/Nosefart NES N2A03 sound emulation core written by
+ Based on the Nofrendo/Nosefart NES RP2A03 sound emulation core written by
Matthew Conte (matt@conte.com) and redesigned for use in MAME/MESS by
Who Wants to Know? (wwtk@mail.com)
This core is written with the advise and consent of Matthew Conte and is
- released under the GNU Public License. This core is freely available for
- use in any freeware project, subject to the following terms:
-
- Any modifications to this code must be duly noted in the source and
- approved by Matthew Conte and myself prior to public submission.
+ released under the GNU Public License.
*****************************************************************************
@@ -52,7 +48,7 @@ public:
virtual void device_reset() override;
virtual void device_clock_changed() override;
- u8 read(offs_t offset);
+ u8 status_r();
void write(offs_t offset, u8 data);
protected:
@@ -64,6 +60,8 @@ protected:
// sound stream update overrides
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
+ virtual void update_lfsr(apu_t::noise_t &chan);
+
private:
/* GLOBAL CONSTANTS */
static constexpr unsigned SYNCS_MAX1 = 0x20;
@@ -73,7 +71,7 @@ private:
// internal state
apu_t m_APU; /* Actual APUs */
- int m_is_pal;
+ u8 m_is_pal;
u32 m_samps_per_sync; /* Number of samples per vsync */
u32 m_vbl_times[SYNCS_MAX1]; /* VBL durations in samples */
u32 m_sync_times1[SYNCS_MAX1]; /* Samples per sync table */
@@ -85,6 +83,11 @@ private:
devcb_write_line m_irq_handler;
devcb_read8 m_mem_read_cb;
+ emu_timer *m_frame_timer;
+ attotime m_frame_period;
+ u16 m_frame_clocks;
+
+ TIMER_CALLBACK_MEMBER(frame_timer_cb);
void calculate_rates();
void apu_square(apu_t::square_t *chan);
void apu_triangle(apu_t::triangle_t *chan);
@@ -92,6 +95,17 @@ private:
void apu_dpcm(apu_t::dpcm_t *chan);
};
-DECLARE_DEVICE_TYPE(NES_APU, nesapu_device)
+class apu2a03_device : public nesapu_device
+{
+public:
+ apu2a03_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+
+protected:
+ virtual void update_lfsr(apu_t::noise_t &chan) override;
+};
+
+
+DECLARE_DEVICE_TYPE(NES_APU, nesapu_device)
+DECLARE_DEVICE_TYPE(APU_2A03, apu2a03_device)
#endif // MAME_SOUND_NES_APU_H
diff --git a/src/devices/sound/nes_apu_vt.cpp b/src/devices/sound/nes_apu_vt.cpp
index f93c3203bac..8216cd3c8f2 100644
--- a/src/devices/sound/nes_apu_vt.cpp
+++ b/src/devices/sound/nes_apu_vt.cpp
@@ -8,7 +8,7 @@
// TODO: everything
#include "emu.h"
-#include "sound/nes_apu_vt.h"
+#include "nes_apu_vt.h"
DEFINE_DEVICE_TYPE(NES_APU_VT, nes_apu_vt_device, "nes_apu_vt", "XOP APU")
diff --git a/src/devices/sound/nes_defs.h b/src/devices/sound/nes_defs.h
index 1f4544b02c2..7756a9b3d18 100644
--- a/src/devices/sound/nes_defs.h
+++ b/src/devices/sound/nes_defs.h
@@ -4,16 +4,12 @@
MAME/MESS NES APU CORE
- Based on the Nofrendo/Nosefart NES N2A03 sound emulation core written by
+ Based on the Nofrendo/Nosefart NES RP2A03 sound emulation core written by
Matthew Conte (matt@conte.com) and redesigned for use in MAME/MESS by
Who Wants to Know? (wwtk@mail.com)
This core is written with the advise and consent of Matthew Conte and is
- released under the GNU Public License. This core is freely available for
- use in any freeware project, subject to the following terms:
-
- Any modifications to this code must be duly noted in the source and
- approved by Matthew Conte and myself prior to public submission.
+ released under the GNU Public License.
*****************************************************************************
@@ -86,7 +82,7 @@ struct apu_t
}
u8 regs[4]; /* regs[1] unused */
- u32 seed = 1;
+ u16 lfsr = 1;
int vbl_length = 0;
float phaseacc = 0.0;
float env_phase = 0.0;
@@ -145,7 +141,9 @@ struct apu_t
noise_t noi;
dpcm_t dpcm;
- int step_mode = 0;
+ u8 step_mode = 0;
+ bool frame_irq_enabled = false;
+ bool frame_irq_occurred = false;
};
/* CONSTANTS */
diff --git a/src/devices/sound/nn71003f.cpp b/src/devices/sound/nn71003f.cpp
new file mode 100644
index 00000000000..b9d926a9855
--- /dev/null
+++ b/src/devices/sound/nn71003f.cpp
@@ -0,0 +1,84 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Nippon Steel Corp NN71003F mpeg audio decoder
+
+// No info could be found anywhere. Function has (minimally) been
+// found from pin connection tracing on a hrdvd board.
+
+#include "emu.h"
+#include "nn71003f.h"
+
+DEFINE_DEVICE_TYPE(NN71003F, nn71003f_device, "nn71003f", "NN71003F mpeg audio chip")
+
+nn71003f_device::nn71003f_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, NN71003F, tag, owner, clock),
+ device_sound_interface(mconfig, *this),
+ m_miso(*this)
+{
+}
+
+void nn71003f_device::device_start()
+{
+ save_item(NAME(m_ss));
+ save_item(NAME(m_sclk));
+ save_item(NAME(m_mosi));
+}
+
+void nn71003f_device::device_reset()
+{
+ m_ss = 0;
+ m_sclk = 0;
+ m_mosi = 0;
+}
+
+void nn71003f_device::ss_w(int state)
+{
+ if(state == m_ss)
+ return;
+ m_ss = state;
+ if(!m_ss)
+ m_spi_cnt = 0;
+}
+
+void nn71003f_device::sclk_w(int state)
+{
+ if(state == m_sclk)
+ return;
+ m_sclk = state;
+ if(!m_sclk)
+ return;
+ m_spi_byte = (m_spi_byte << 1) | m_mosi;
+ m_spi_cnt ++;
+ if(m_spi_cnt & 7)
+ return;
+
+ logerror("SPI %x: %02x\n", m_spi_cnt >> 3, m_spi_byte);
+}
+
+void nn71003f_device::mosi_w(int state)
+{
+ if(state == m_mosi)
+ return;
+ m_mosi = state;
+}
+
+void nn71003f_device::frm_w(int state)
+{
+ logerror("frm_w %d\n", state);
+}
+
+void nn71003f_device::dat_w(int state)
+{
+ logerror("dat_w %d\n", state);
+}
+
+void nn71003f_device::clk_w(int state)
+{
+ logerror("clk_w %d\n", state);
+}
+
+void nn71003f_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
+{
+}
+
diff --git a/src/devices/sound/nn71003f.h b/src/devices/sound/nn71003f.h
new file mode 100644
index 00000000000..870125e723e
--- /dev/null
+++ b/src/devices/sound/nn71003f.h
@@ -0,0 +1,42 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Nippon Steel Corp NN71003F mpeg audio decoder
+
+#ifndef DEVICES_SOUND_NN71003F_H
+#define DEVICES_SOUND_NN71003F_H
+
+#pragma once
+
+#include "mpeg_audio.h"
+
+class nn71003f_device : public device_t, public device_sound_interface
+{
+public:
+ nn71003f_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
+
+ // Serial audio interface
+ void frm_w(int state);
+ void dat_w(int state);
+ void clk_w(int state);
+
+ // Slave SPI interface
+ void ss_w(int state);
+ void sclk_w(int state);
+ void mosi_w(int state);
+ auto miso_cb() { return m_miso.bind(); }
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
+
+private:
+ devcb_write_line m_miso;
+ u8 m_spi_byte, m_spi_cnt;
+ int m_ss, m_sclk, m_mosi;
+};
+
+DECLARE_DEVICE_TYPE(NN71003F, nn71003f_device)
+
+#endif
diff --git a/src/devices/sound/okiadpcm.cpp b/src/devices/sound/okiadpcm.cpp
index c9702f93175..631a317724f 100644
--- a/src/devices/sound/okiadpcm.cpp
+++ b/src/devices/sound/okiadpcm.cpp
@@ -17,6 +17,7 @@
Application JP,1995-104333 (Unexamined Publication JP,H08-307371,A) (Not examined or registered) https://patents.google.com/patent/JPH08307371A/en <- something unrelated to adpcm, wireless transmission error detection related?
Application JP,1995-162009 (Unexamined Publication JP,H09-018425,A) (Not examined or registered) https://patents.google.com/patent/JPH0918425A/en <- looks like ADPCM2 maybe?
Application JP,1988-176215 (Unexamined Publication JP,H02-026426,A) (Not examined or registered) https://patents.google.com/patent/JPH0226426A/en <- Fujitsu variant on (G.726/727?) SB-ADPCM, cited by above
+
***************************************************************************/
#include "emu.h"
@@ -131,7 +132,7 @@ void oki_adpcm_state::compute_tables()
for (int nib = 0; nib < 16; nib++)
{
s_diff_lookup[step*16 + nib] = nbl2bit[nib][0] *
- (stepval * nbl2bit[nib][1] +
+ (stepval * nbl2bit[nib][1] +
stepval/2 * nbl2bit[nib][2] +
stepval/4 * nbl2bit[nib][3] +
stepval/8);
@@ -250,7 +251,7 @@ void oki_adpcm2_state::compute_tables()
for (int nib = 0; nib < 16; nib++)
{
s_diff_lookup[step*16 + nib] = nbl2bit[nib][0] *
- (stepval * nbl2bit[nib][1] +
+ (stepval * nbl2bit[nib][1] +
stepval/2 * nbl2bit[nib][2] +
stepval/4 * nbl2bit[nib][3] +
stepval/8);
diff --git a/src/devices/sound/okim6258.cpp b/src/devices/sound/okim6258.cpp
index 349fe9ca13c..171100e2080 100644
--- a/src/devices/sound/okim6258.cpp
+++ b/src/devices/sound/okim6258.cpp
@@ -2,15 +2,15 @@
// copyright-holders:Barry Rodewald
/**********************************************************************************************
*
- * OKI MSM6258 ADPCM
+ * OKI MSM6258 ADPCM Speech Processor
*
* TODO:
- * 3-bit ADPCM support
- * Recording?
+ * - 3-bit ADPCM support
+ * - Use okiadpcm.* helper?
+ * - Recording?
*
**********************************************************************************************/
-
#include "emu.h"
#include "okim6258.h"
@@ -46,19 +46,19 @@ DEFINE_DEVICE_TYPE(OKIM6258, okim6258_device, "okim6258", "OKI MSM6258 ADPCM")
// okim6258_device - constructor
//-------------------------------------------------
-okim6258_device::okim6258_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, OKIM6258, tag, owner, clock),
- device_sound_interface(mconfig, *this),
- m_status(0),
- m_start_divider(0),
- m_divider(512),
- m_adpcm_type(0),
- m_data_in(0),
- m_nibble_shift(0),
- m_stream(nullptr),
- m_output_bits(0),
- m_signal(0),
- m_step(0)
+okim6258_device::okim6258_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, OKIM6258, tag, owner, clock),
+ device_sound_interface(mconfig, *this),
+ m_status(0),
+ m_start_divider(0),
+ m_divider(512),
+ m_adpcm_type(0),
+ m_data_in(0),
+ m_nibble_shift(0),
+ m_stream(nullptr),
+ m_output_bits(0),
+ m_signal(0),
+ m_step(0)
{
}
@@ -93,7 +93,7 @@ static void compute_tables()
for (nib = 0; nib < 16; nib++)
{
diff_lookup[step*16 + nib] = nbl2bit[nib][0] *
- (stepval * nbl2bit[nib][1] +
+ (stepval * nbl2bit[nib][1] +
stepval/2 * nbl2bit[nib][2] +
stepval/4 * nbl2bit[nib][3] +
stepval/8);
@@ -119,7 +119,13 @@ void okim6258_device::device_start()
m_signal = -2;
m_step = 0;
- state_save_register();
+ // register for savestates
+ save_item(NAME(m_status));
+ save_item(NAME(m_divider));
+ save_item(NAME(m_data_in));
+ save_item(NAME(m_nibble_shift));
+ save_item(NAME(m_signal));
+ save_item(NAME(m_step));
}
@@ -171,25 +177,6 @@ void okim6258_device::sound_stream_update(sound_stream &stream, std::vector<read
}
}
-
-
-/**********************************************************************************************
-
- state save support for MAME
-
-***********************************************************************************************/
-
-void okim6258_device::state_save_register()
-{
- save_item(NAME(m_status));
- save_item(NAME(m_divider));
- save_item(NAME(m_data_in));
- save_item(NAME(m_nibble_shift));
- save_item(NAME(m_signal));
- save_item(NAME(m_step));
-}
-
-
int16_t okim6258_device::clock_adpcm(uint8_t nibble)
{
int32_t max = (1 << (m_output_bits - 1)) - 1;
@@ -217,7 +204,7 @@ int16_t okim6258_device::clock_adpcm(uint8_t nibble)
/**********************************************************************************************
- okim6258::set_divider -- set the master clock divider
+ set_divider -- set the master clock divider
***********************************************************************************************/
@@ -230,7 +217,7 @@ void okim6258_device::set_divider(int val)
/**********************************************************************************************
- okim6258::set_clock -- set the master clock
+ set_clock -- set the master clock
***********************************************************************************************/
@@ -242,7 +229,7 @@ void okim6258_device::device_clock_changed()
/**********************************************************************************************
- okim6258::get_vclk -- get the VCLK/sampling frequency
+ get_vclk -- get the VCLK/sampling frequency
***********************************************************************************************/
@@ -254,7 +241,7 @@ int okim6258_device::get_vclk()
/**********************************************************************************************
- okim6258_status_r -- read the status port of an OKIM6258-compatible chip
+ status_r -- read the status port of an OKIM6258-compatible chip
***********************************************************************************************/
@@ -268,9 +255,10 @@ uint8_t okim6258_device::status_r()
/**********************************************************************************************
- okim6258_data_w -- write to the control port of an OKIM6258-compatible chip
+ data_w -- write to the control port of an OKIM6258-compatible chip
***********************************************************************************************/
+
void okim6258_device::data_w(uint8_t data)
{
/* update the stream */
@@ -283,7 +271,7 @@ void okim6258_device::data_w(uint8_t data)
/**********************************************************************************************
- okim6258_ctrl_w -- write to the control port of an OKIM6258-compatible chip
+ ctrl_w -- write to the control port of an OKIM6258-compatible chip
***********************************************************************************************/
diff --git a/src/devices/sound/okim6258.h b/src/devices/sound/okim6258.h
index 95189f0ef3c..e7e4a25c414 100644
--- a/src/devices/sound/okim6258.h
+++ b/src/devices/sound/okim6258.h
@@ -50,20 +50,18 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
private:
- void state_save_register();
int16_t clock_adpcm(uint8_t nibble);
uint8_t m_status;
uint32_t m_start_divider;
- uint32_t m_divider; /* master clock divider */
- uint8_t m_adpcm_type; /* 3/4 bit ADPCM select */
- uint8_t m_data_in; /* ADPCM data-in register */
- uint8_t m_nibble_shift; /* nibble select */
- sound_stream *m_stream; /* which stream are we playing on? */
-
- uint8_t m_output_bits; /* D/A precision is 10-bits but 12-bit data can be
- output serially to an external DAC */
+ uint32_t m_divider; // master clock divider
+ uint8_t m_adpcm_type; // 3/4 bit ADPCM select
+ uint8_t m_data_in; // ADPCM data-in register
+ uint8_t m_nibble_shift; // nibble select
+ sound_stream *m_stream; // which stream are we playing on?
+
+ uint8_t m_output_bits; // D/A precision is 10-bits but 12-bit data can be output serially to an external DAC
int32_t m_signal;
int32_t m_step;
diff --git a/src/devices/sound/okim6295.cpp b/src/devices/sound/okim6295.cpp
index 4d304ef1721..d4006fea1da 100644
--- a/src/devices/sound/okim6295.cpp
+++ b/src/devices/sound/okim6295.cpp
@@ -85,14 +85,14 @@ const stream_buffer::sample_t okim6295_device::s_volume_table[16] =
// okim6295_device - constructor
//-------------------------------------------------
-okim6295_device::okim6295_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, OKIM6295, tag, owner, clock),
- device_sound_interface(mconfig, *this),
- device_rom_interface(mconfig, *this),
- m_region(*this, DEVICE_SELF),
- m_command(-1),
- m_stream(nullptr),
- m_pin7_state(~uint8_t(0))
+okim6295_device::okim6295_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, OKIM6295, tag, owner, clock),
+ device_sound_interface(mconfig, *this),
+ device_rom_interface(mconfig, *this),
+ m_region(*this, DEVICE_SELF),
+ m_command(-1),
+ m_stream(nullptr),
+ m_pin7_state(~uint8_t(0))
{
}
@@ -190,10 +190,11 @@ void okim6295_device::sound_stream_update(sound_stream &stream, std::vector<read
//-------------------------------------------------
-// rom_bank_updated - the rom bank has changed
+// rom_bank_pre_change - refresh the stream if the
+// ROM banking changes
//-------------------------------------------------
-void okim6295_device::rom_bank_updated()
+void okim6295_device::rom_bank_pre_change()
{
m_stream->update();
}
@@ -325,12 +326,12 @@ void okim6295_device::write(uint8_t command)
// okim_voice - constructor
//-------------------------------------------------
-okim6295_device::okim_voice::okim_voice()
- : m_playing(false),
- m_base_offset(0),
- m_sample(0),
- m_count(0),
- m_volume(0)
+okim6295_device::okim_voice::okim_voice() :
+ m_playing(false),
+ m_base_offset(0),
+ m_sample(0),
+ m_count(0),
+ m_volume(0)
{
}
diff --git a/src/devices/sound/okim6295.h b/src/devices/sound/okim6295.h
index 29f0a17f1a1..4d372a8a47c 100644
--- a/src/devices/sound/okim6295.h
+++ b/src/devices/sound/okim6295.h
@@ -63,7 +63,7 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
// device_rom_interface overrides
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
// a single voice
class okim_voice
diff --git a/src/devices/sound/okim6376.cpp b/src/devices/sound/okim6376.cpp
index 997b3e8b13f..7807a163e71 100644
--- a/src/devices/sound/okim6376.cpp
+++ b/src/devices/sound/okim6376.cpp
@@ -196,8 +196,9 @@ void okim6376_device::device_reset()
}
-void okim6376_device::rom_bank_updated()
+void okim6376_device::rom_bank_pre_change()
{
+ m_stream->update();
}
@@ -279,51 +280,51 @@ void okim6376_device::oki_process(int channel, int command)
// determine the start position
offs_t start = get_start_position(channel);
- if (start == 0)
+ if (start == 0)
+ {
+ voice->playing = 0;
+ }
+ else
+ {
+ /* set up the voice to play this sample */
+ if (!voice->playing)
{
- voice->playing = 0;
+ voice->playing = 1;
+ voice->base_offset = start;
+ voice->sample = 0;
+ voice->count = 0;
+
+ /* also reset the ADPCM parameters */
+ voice->reset();
+ if (channel == 0)
+ {
+ /* We set channel 2's audio separately */
+ voice->volume = volume_table[0];
+ }
}
else
{
- /* set up the voice to play this sample */
- if (!voice->playing)
- {
- voice->playing = 1;
- voice->base_offset = start;
- voice->sample = 0;
- voice->count = 0;
-
- /* also reset the ADPCM parameters */
- voice->reset();
- if (channel == 0)
- {
- /* We set channel 2's audio separately */
- voice->volume = volume_table[0];
- }
- }
- else
+ if (((m_nar)&&(channel == 0))||(channel == 1))//Store the request, for later processing (channel 2 ignores NAR)
{
- if (((m_nar)&&(channel == 0))||(channel == 1))//Store the request, for later processing (channel 2 ignores NAR)
- {
- m_stage[channel] = 1;
- }
+ m_stage[channel] = 1;
}
}
}
}
- /* otherwise, see if this is a silence command */
- else
- {
- /* update the stream, then turn it off */
- m_stream->update();
+ }
+ /* otherwise, see if this is a silence command */
+ else
+ {
+ /* update the stream, then turn it off */
+ m_stream->update();
- if (command ==0)
+ if (command ==0)
+ {
+ int i;
+ for (i = 0; i < OKIM6376_VOICES; i++)
{
- int i;
- for (i = 0; i < OKIM6376_VOICES; i++)
- {
- struct ADPCMVoice *voice = &m_voice[i];
- voice->playing = 0;
+ struct ADPCMVoice *voice = &m_voice[i];
+ voice->playing = 0;
}
}
}
@@ -421,8 +422,7 @@ void okim6376_device::adpcm_state_save_register(struct ADPCMVoice *voice, int in
void okim6376_device::okim6376_state_save_register()
{
- int j;
- for (j = 0; j < OKIM6376_VOICES; j++)
+ for (int j = 0; j < OKIM6376_VOICES; j++)
{
adpcm_state_save_register(&m_voice[j], j);
}
@@ -455,7 +455,7 @@ void okim6376_device::device_clock_changed()
***********************************************************************************************/
-READ_LINE_MEMBER( okim6376_device::busy_r )
+int okim6376_device::busy_r()
{
struct ADPCMVoice *voice0 = &m_voice[0];
struct ADPCMVoice *voice1 = &m_voice[1];
@@ -471,13 +471,13 @@ READ_LINE_MEMBER( okim6376_device::busy_r )
}
}
-READ_LINE_MEMBER( okim6376_device::nar_r )
+int okim6376_device::nar_r()
{
LOG("OKIM6376: NAR %x\n",m_nar);
return m_nar;
}
-WRITE_LINE_MEMBER( okim6376_device::ch2_w )
+void okim6376_device::ch2_w(int state)
{
m_ch2_update = 0;//Clear flag
LOG("OKIM6376: CH2 %x\n",state);
@@ -514,7 +514,7 @@ WRITE_LINE_MEMBER( okim6376_device::ch2_w )
}
-WRITE_LINE_MEMBER( okim6376_device::st_w )
+void okim6376_device::st_w(int state)
{
//As in STart, presumably, this triggers everything
@@ -556,7 +556,7 @@ WRITE_LINE_MEMBER( okim6376_device::st_w )
}
-WRITE_LINE_MEMBER( okim6650_device::cmd_w )
+void okim6650_device::cmd_w(int state)
{
// TODO
}
diff --git a/src/devices/sound/okim6376.h b/src/devices/sound/okim6376.h
index 34a23764e3a..ea90a7e51ea 100644
--- a/src/devices/sound/okim6376.h
+++ b/src/devices/sound/okim6376.h
@@ -16,11 +16,11 @@ public:
void write(uint8_t data);
- DECLARE_WRITE_LINE_MEMBER( st_w );
- DECLARE_WRITE_LINE_MEMBER( ch2_w );
+ void st_w(int state);
+ void ch2_w(int state);
- DECLARE_READ_LINE_MEMBER( busy_r );
- DECLARE_READ_LINE_MEMBER( nar_r );
+ int busy_r();
+ int nar_r();
protected:
okim6376_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, int addrbits);
@@ -35,7 +35,7 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
// device_rom_interface overrides
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
virtual offs_t get_start_position(int channel);
virtual u32 get_sample_rate();
@@ -86,7 +86,7 @@ class okim6650_device : public okim6376_device
public:
okim6650_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
- DECLARE_WRITE_LINE_MEMBER( cmd_w );
+ void cmd_w(int state);
protected:
virtual offs_t get_start_position(int channel) override;
diff --git a/src/devices/sound/okim6588.cpp b/src/devices/sound/okim6588.cpp
new file mode 100644
index 00000000000..de12b18664b
--- /dev/null
+++ b/src/devices/sound/okim6588.cpp
@@ -0,0 +1,232 @@
+// license:BSD-3-Clause
+// copyright-holders:hap
+/*
+
+OKI MSM6588 ADPCM Recorder
+
+It has similar functionality to MSM6258.
+
+TODO:
+- it only supports MCU mode EXT playback, nothing else emulated yet
+- status register read (eg. BUSY flag)
+
+*/
+
+#include "emu.h"
+#include "okim6588.h"
+
+
+DEFINE_DEVICE_TYPE(OKIM6588, okim6588_device, "okim6588", "OKI MSM6588 ADPCM Recorder")
+
+//-------------------------------------------------
+// constructor
+//-------------------------------------------------
+
+okim6588_device::okim6588_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ device_t(mconfig, OKIM6588, tag, owner, clock),
+ device_sound_interface(mconfig, *this),
+ m_write_mon(*this),
+ m_chip_mode(CHIP_MODE_STANDALONE)
+{ }
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+// allow save_item on a non-fundamental type
+ALLOW_SAVE_TYPE(okim6588_device::chip_mode);
+ALLOW_SAVE_TYPE(okim6588_device::command_state);
+ALLOW_SAVE_TYPE(okim6588_device::run_state);
+
+void okim6588_device::device_start()
+{
+ // initialize
+ m_stream = stream_alloc(0, 1, clock() / 128);
+
+ m_adpcm_timer = timer_alloc(FUNC(okim6588_device::clock_adpcm), this);
+ m_mon_timer = timer_alloc(FUNC(okim6588_device::set_mon), this);
+
+ m_command_state = COMMAND_READY;
+ m_run_state = RUN_STOP;
+ m_adpcm_data = 0;
+
+ m_vds_bit = (m_chip_mode == CHIP_MODE_MCU) ? 1 : 0;
+ m_samp_fdiv = 512;
+ m_rec_mode = false;
+
+ // register for savestates
+ save_item(NAME(m_chip_mode));
+ save_item(NAME(m_command_state));
+ save_item(NAME(m_run_state));
+ save_item(NAME(m_adpcm_data));
+ save_item(NAME(m_adpcm.m_signal));
+ save_item(NAME(m_adpcm.m_step));
+ save_item(NAME(m_rec_mode));
+ save_item(NAME(m_samp_fdiv));
+ save_item(NAME(m_vds_bit));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void okim6588_device::device_reset()
+{
+ m_command_state = COMMAND_READY;
+ m_run_state = RUN_STOP;
+ reset_adpcm();
+
+ m_adpcm_timer->adjust(attotime::never);
+ m_mon_timer->adjust(attotime::never);
+ m_write_mon(0);
+}
+
+
+//-------------------------------------------------
+// internal handlers
+//-------------------------------------------------
+
+void okim6588_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
+{
+ // simply fill the buffer with the current sample
+ outputs[0].fill(m_adpcm.output() / 2048.0);
+}
+
+TIMER_CALLBACK_MEMBER(okim6588_device::clock_adpcm)
+{
+ switch (m_run_state)
+ {
+ case RUN_STOP:
+ reset_adpcm();
+ break;
+
+ case RUN_PLAY_EXT:
+ // strobe MON
+ m_write_mon(1);
+ m_mon_timer->adjust(attotime::from_ticks(m_samp_fdiv / 4, clock()), 0);
+ m_command_state = COMMAND_EXT;
+
+ m_stream->update();
+ get_adpcm_sample(m_adpcm_data);
+ break;
+
+ default:
+ break;
+ }
+
+ if (m_run_state != RUN_STOP && m_run_state != RUN_PAUSE)
+ m_adpcm_timer->adjust(attotime::from_ticks(m_samp_fdiv, clock()));
+}
+
+TIMER_CALLBACK_MEMBER(okim6588_device::set_mon)
+{
+ m_write_mon(param ? 1 : 0);
+}
+
+s16 okim6588_device::get_adpcm_sample(u8 data)
+{
+ // 4-bit or 3-bit input
+ if (m_vds_bit)
+ return m_adpcm.clock(data & 0xf);
+ else
+ return m_adpcm.clock((data & 0xc) | (data >> 1 & 1));
+}
+
+void okim6588_device::reset_adpcm()
+{
+ if (machine().time() > attotime::zero)
+ m_stream->update();
+
+ m_adpcm_data = 0;
+ m_adpcm.reset();
+}
+
+
+//-------------------------------------------------
+// public handlers
+//-------------------------------------------------
+
+u8 okim6588_device::data_r()
+{
+ if (m_chip_mode != CHIP_MODE_MCU)
+ return 0;
+
+ return 0;
+}
+
+void okim6588_device::data_w(u8 data)
+{
+ if (m_chip_mode != CHIP_MODE_MCU)
+ return;
+
+ data &= 0xf;
+
+ switch (m_command_state)
+ {
+ case COMMAND_READY:
+ switch (data & 0xf)
+ {
+ // NOP
+ case 0x0:
+ break;
+
+ // PLAY/REC
+ case 0x2: case 0x3:
+ m_rec_mode = bool(data & 1);
+ break;
+
+ // STOP
+ case 0x5:
+ m_run_state = RUN_STOP;
+ break;
+
+ // SAMP
+ case 0x6:
+ m_command_state = COMMAND_SAMP;
+ break;
+
+ // VDS
+ case 0xc:
+ m_command_state = COMMAND_VDS;
+ break;
+
+ // EXT
+ case 0xb:
+ m_run_state = m_rec_mode ? RUN_RECORD_EXT : RUN_PLAY_EXT;
+ reset_adpcm();
+
+ // minimum delay is 1 sample
+ m_adpcm_timer->adjust(attotime::from_ticks(m_samp_fdiv, clock()));
+ m_command_state = COMMAND_EXT;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case COMMAND_SAMP:
+ {
+ static const u16 div[4] = { 1024, 768, 640, 512 };
+ m_samp_fdiv = div[data & 3];
+ m_command_state = COMMAND_READY;
+ break;
+ }
+
+ case COMMAND_EXT:
+ m_adpcm_data = data;
+ m_command_state = COMMAND_READY;
+ break;
+
+ case COMMAND_VDS:
+ m_vds_bit = BIT(data, 2);
+ m_command_state = COMMAND_READY;
+ break;
+
+ default:
+ // shouldn't get here
+ break;
+ }
+}
diff --git a/src/devices/sound/okim6588.h b/src/devices/sound/okim6588.h
new file mode 100644
index 00000000000..28bcec029aa
--- /dev/null
+++ b/src/devices/sound/okim6588.h
@@ -0,0 +1,88 @@
+// license:BSD-3-Clause
+// copyright-holders:hap
+/*
+
+ OKI MSM6588 ADPCM Recorder
+
+*/
+
+#ifndef MAME_SOUND_OKIM6588_H
+#define MAME_SOUND_OKIM6588_H
+
+#pragma once
+
+#include "sound/okiadpcm.h"
+
+
+class okim6588_device : public device_t, public device_sound_interface
+{
+public:
+ okim6588_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+
+ // configuration helpers
+ auto write_mon() { return m_write_mon.bind(); }
+ void set_mcum_pin(int state) { m_chip_mode = state ? CHIP_MODE_MCU : CHIP_MODE_STANDALONE; }
+
+ // D0-D3 (MCU mode)
+ void data_w(u8 data);
+ u8 data_r();
+
+protected:
+ // device_t implementation
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
+
+private:
+ enum chip_mode : u8
+ {
+ CHIP_MODE_STANDALONE = 0,
+ CHIP_MODE_MCU
+ };
+
+ enum command_state : u8
+ {
+ COMMAND_READY = 0,
+ COMMAND_SAMP,
+ COMMAND_EXT,
+ COMMAND_VDS
+ };
+
+ enum run_state : u8
+ {
+ RUN_STOP = 0,
+ RUN_PAUSE,
+ RUN_PLAY_SERIAL,
+ RUN_PLAY_EXT,
+ RUN_RECORD_SERIAL,
+ RUN_RECORD_EXT,
+ };
+
+ devcb_write_line m_write_mon;
+
+ chip_mode m_chip_mode;
+ command_state m_command_state;
+ run_state m_run_state;
+
+ sound_stream *m_stream;
+
+ u8 m_adpcm_data;
+ oki_adpcm_state m_adpcm;
+ bool m_rec_mode;
+ u16 m_samp_fdiv;
+ u8 m_vds_bit;
+
+ emu_timer *m_adpcm_timer;
+ emu_timer *m_mon_timer;
+
+ TIMER_CALLBACK_MEMBER(clock_adpcm);
+ TIMER_CALLBACK_MEMBER(set_mon);
+ s16 get_adpcm_sample(u8 data);
+ void reset_adpcm();
+};
+
+
+DECLARE_DEVICE_TYPE(OKIM6588, okim6588_device)
+
+#endif // MAME_SOUND_OKIM6588_H
diff --git a/src/devices/sound/okim9810.cpp b/src/devices/sound/okim9810.cpp
index f7a9f6d6f09..db658eacf00 100644
--- a/src/devices/sound/okim9810.cpp
+++ b/src/devices/sound/okim9810.cpp
@@ -200,10 +200,11 @@ void okim9810_device::device_clock_changed()
//-------------------------------------------------
-// rom_bank_updated - the rom bank has changed
+// rom_bank_pre_change - refresh the stream if the
+// ROM banking changes
//-------------------------------------------------
-void okim9810_device::rom_bank_updated()
+void okim9810_device::rom_bank_pre_change()
{
m_stream->update();
}
diff --git a/src/devices/sound/okim9810.h b/src/devices/sound/okim9810.h
index 01977b23e2b..18db148aa40 100644
--- a/src/devices/sound/okim9810.h
+++ b/src/devices/sound/okim9810.h
@@ -90,7 +90,7 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
// device_rom_interface overrides
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
// a single voice
class okim_voice
diff --git a/src/devices/sound/pokey.cpp b/src/devices/sound/pokey.cpp
index d80e17f74d2..35f389fcd58 100644
--- a/src/devices/sound/pokey.cpp
+++ b/src/devices/sound/pokey.cpp
@@ -2,15 +2,34 @@
// copyright-holders:Brad Oliver, Eric Smith, Juergen Buchmueller
/*****************************************************************************
*
- * POKEY chip emulator 4.6
+ * POKEY chip emulator 4.9
*
* Based on original info found in Ron Fries' Pokey emulator,
* with additions by Brad Oliver, Eric Smith and Juergen Buchmueller,
* paddle (a/d conversion) details from the Atari 400/800 Hardware Manual.
* Polynomial algorithms according to info supplied by Perry McFarlane.
+ * Additional improvements from Mike Saarna's A7800 MAME fork.
+ *
+ * 4.9:
+ * - Two-tone mode updated for better accuracy.
+ *
+ * 4.8:
+ * - Poly5 related modes had a pitch shift issue. The poly4/5 init routine
+ * was replaced with one based on Altira's implementation, which resolved
+ * the issue.
+ *
+ * 4.7:
+ * [1] https://www.virtualdub.org/downloads/Altirra%20Hardware%20Reference%20Manual.pdf
+ * - updated to reflect that borrowing cycle delays only impacts voices
+ * running at 1.79MHz. (+4 cycles unlinked, or +7 cycles linked)
+ * At slower speeds, cycle overhead still occurs, but only affects
+ * the phase of the timer period, not the actual length.
+ * - Initial two-tone support added. Emulation of two-tone is limited to
+ * audio output effects, and doesn't incorporate any of the aspects of
+ * SIO serial transfer.
*
* 4.6:
- * [1] http://ploguechipsounds.blogspot.de/2009/10/how-i-recorded-and-decoded-pokeys.html
+ * [2] http://ploguechipsounds.blogspot.de/2009/10/how-i-recorded-and-decoded-pokeys.html
* - changed audio emulation to emulate borrow 3 clock delay and
* proper channel reset. New frequency only becomes effective
* after the counter hits 0. Emulation also treats counters
@@ -19,6 +38,7 @@
*
* 4.51:
* - changed to use the attotime datatype
+ *
* 4.5:
* - changed the 9/17 bit polynomial formulas such that the values
* required for the Tempest Pokey protection will be found.
@@ -29,22 +49,27 @@
* - reading the RNG returns the shift register contents ^ 0xff.
* That way resetting the Pokey with SKCTL (which resets the
* polynomial shifters to 0) returns the expected 0xff value.
+ *
* 4.4:
* - reversed sample values to make OFF channels produce a zero signal.
* actually de-reversed them; don't remember that I reversed them ;-/
+ *
* 4.3:
* - for POT inputs returning zero, immediately assert the ALLPOT
* bit after POTGO is written, otherwise start trigger timer
* depending on SK_PADDLE mode, either 1-228 scanlines or 1-2
* scanlines, depending on the SK_PADDLE bit of SKCTL.
+ *
* 4.2:
* - half volume for channels which are inaudible (this should be
* close to the real thing).
+ *
* 4.1:
* - default gain increased to closely match the old code.
* - random numbers repeat rate depends on POLY9 flag too!
* - verified sound output with many, many Atari 800 games,
* including the SUPPRESS_INAUDIBLE optimizations.
+ *
* 4.0:
* - rewritten from scratch.
* - 16bit stream interface.
@@ -75,21 +100,24 @@
#define POKEY_DEFAULT_GAIN (32767/11/4)
-#define VERBOSE 0
-#define VERBOSE_SOUND 0
-#define VERBOSE_TIMER 0
-#define VERBOSE_POLY 0
-#define VERBOSE_RAND 0
+#define VERBOSE_SOUND (1U << 1)
+#define VERBOSE_TIMER (1U << 2)
+#define VERBOSE_POLY (1U << 3)
+#define VERBOSE_RAND (1U << 4)
+#define VERBOSE_IRQ (1U << 5)
+#define VERBOSE (0)
+
+#include "logmacro.h"
-#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+#define LOG_SOUND(...) LOGMASKED(VERBOSE_SOUND, __VA_ARGS__)
-#define LOG_SOUND(x) do { if (VERBOSE_SOUND) logerror x; } while (0)
+#define LOG_TIMER(...) LOGMASKED(VERBOSE_TIMER, __VA_ARGS__)
-#define LOG_TIMER(x) do { if (VERBOSE_TIMER) logerror x; } while (0)
+#define LOG_POLY(...) LOGMASKED(VERBOSE_POLY, __VA_ARGS__)
-#define LOG_POLY(x) do { if (VERBOSE_POLY) logerror x; } while (0)
+#define LOG_RAND(...) LOGMASKED(VERBOSE_RAND, __VA_ARGS__)
-#define LOG_RAND(x) do { if (VERBOSE_RAND) logerror x; } while (0)
+#define LOG_IRQ(...) LOGMASKED(VERBOSE_IRQ, __VA_ARGS__)
#define CHAN1 0
#define CHAN2 1
@@ -139,7 +167,7 @@
/* SKCTL (W/D20F) */
#define SK_BREAK 0x80 /* serial out break signal */
#define SK_BPS 0x70 /* bits per second */
-#define SK_FM 0x08 /* FM mode */
+#define SK_TWOTONE 0x08 /* Two tone mode */
#define SK_PADDLE 0x04 /* fast paddle a/d conversion */
#define SK_RESET 0x03 /* reset serial/keyboard interface */
#define SK_KEYSCAN 0x02 /* key scanning enabled ? */
@@ -152,9 +180,6 @@
#define CLK_28 1
#define CLK_114 2
-constexpr unsigned pokey_device::FREQ_17_EXACT;
-
-
// device type definition
DEFINE_DEVICE_TYPE(POKEY, pokey_device, "pokey", "Atari C012294 POKEY")
@@ -167,23 +192,23 @@ DEFINE_DEVICE_TYPE(POKEY, pokey_device, "pokey", "Atari C012294 POKEY")
// pokey_device - constructor
//-------------------------------------------------
-pokey_device::pokey_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, POKEY, tag, owner, clock),
- device_sound_interface(mconfig, *this),
- device_execute_interface(mconfig, *this),
- device_state_interface(mconfig, *this),
- m_icount(0),
- m_stream(nullptr),
- m_pot_r_cb(*this),
- m_allpot_r_cb(*this),
- m_serin_r_cb(*this),
- m_serout_w_cb(*this),
- m_keyboard_r(*this),
- m_irq_f(*this),
- m_output_type(LEGACY_LINEAR),
- m_serout_ready_timer(nullptr),
- m_serout_complete_timer(nullptr),
- m_serin_ready_timer(nullptr)
+pokey_device::pokey_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, POKEY, tag, owner, clock),
+ device_sound_interface(mconfig, *this),
+ device_execute_interface(mconfig, *this),
+ device_state_interface(mconfig, *this),
+ m_icount(0),
+ m_stream(nullptr),
+ m_pot_r_cb(*this, 0),
+ m_allpot_r_cb(*this, 0),
+ m_serin_r_cb(*this, 0),
+ m_serout_w_cb(*this),
+ m_irq_w_cb(*this),
+ m_keyboard_r(*this),
+ m_output_type(LEGACY_LINEAR),
+ m_serout_ready_timer(nullptr),
+ m_serout_complete_timer(nullptr),
+ m_serin_ready_timer(nullptr)
{
}
@@ -195,19 +220,15 @@ void pokey_device::device_start()
{
//int sample_rate = clock();
- /* Setup channels */
- for (int i=0; i<POKEY_CHANNELS; i++)
- {
- m_channel[i].m_parent = this;
- m_channel[i].m_INTMask = 0;
- }
+ // Set up channels
+ for (pokey_channel &chan : m_channel)
+ chan.m_INTMask = 0;
m_channel[CHAN1].m_INTMask = IRQ_TIMR1;
m_channel[CHAN2].m_INTMask = IRQ_TIMR2;
m_channel[CHAN4].m_INTMask = IRQ_TIMR4;
- // bind callbacks
+ // bind delegates
m_keyboard_r.resolve();
- m_irq_f.resolve();
/* calculate the A/D times
* In normal, slow mode (SKCTL bit SK_PADDLE is clear) the conversion
@@ -218,20 +239,29 @@ void pokey_device::device_start()
*/
/* initialize the poly counters */
- poly_init_4_5(m_poly4, 4, 1, 0);
- poly_init_4_5(m_poly5, 5, 2, 1);
+ poly_init_4_5(m_poly4, 4);
+ poly_init_4_5(m_poly5, 5);
/* initialize 9 / 17 arrays */
poly_init_9_17(m_poly9, 9);
poly_init_9_17(m_poly17, 17);
vol_init();
+ for (int i=0; i<4; i++)
+ m_channel[i].m_AUDC = 0xb0;
+
/* The pokey does not have a reset line. These should be initialized
* with random values.
*/
- m_KBCODE = 0x09; /* Atari 800 'no key' */
- m_SKCTL = SK_RESET; /* let the RNG run after reset */
+ m_KBCODE = 0x09; // Atari 800 'no key'
+ m_SKCTL = 0;
+
+ // TODO: several a7800 demos don't explicitly reset pokey at startup
+ // See https://atariage.com/forums/topic/337317-a7800-52-release/ and
+ // https://atariage.com/forums/topic/268458-a7800-the-atari-7800-emulator/?do=findComment&comment=5079170)
+ // m_SKCTL = SK_RESET;
+
m_SKSTAT = 0;
/* This bit should probably get set later. Acid5200 pokey_setoc test tests this. */
m_IRQST = IRQ_SEROC;
@@ -254,11 +284,6 @@ void pokey_device::device_start()
std::fill(std::begin(m_clock_cnt), std::end(m_clock_cnt), 0);
std::fill(std::begin(m_POTx), std::end(m_POTx), 0);
- m_pot_r_cb.resolve_all();
- m_allpot_r_cb.resolve();
- m_serin_r_cb.resolve();
- m_serout_w_cb.resolve_safe();
-
m_stream = stream_alloc(0, 1, clock());
m_serout_ready_timer = timer_alloc(FUNC(pokey_device::serout_ready_irq), this);
@@ -325,6 +350,10 @@ void pokey_device::device_start()
void pokey_device::device_reset()
{
m_stream->update();
+ // a1200xl reads POT4 twice at startup for reading self-test mode jumpers.
+ // we need to update POT counters here otherwise it will boot to self-test
+ // the first time around no matter the setting.
+ pokey_potgo();
}
@@ -364,18 +393,18 @@ TIMER_CALLBACK_MEMBER(pokey_device::serout_ready_irq)
if (m_IRQEN & IRQ_SEROR)
{
m_IRQST |= IRQ_SEROR;
- if (!m_irq_f.isnull())
- m_irq_f(IRQ_SEROR);
+ LOG_IRQ("POKEY SEROR IRQ raised\n");
+ m_irq_w_cb(ASSERT_LINE);
}
}
TIMER_CALLBACK_MEMBER(pokey_device::serout_complete_irq)
{
+ m_IRQST |= IRQ_SEROC;
if (m_IRQEN & IRQ_SEROC)
{
- m_IRQST |= IRQ_SEROC;
- if (!m_irq_f.isnull())
- m_irq_f(IRQ_SEROC);
+ LOG_IRQ("POKEY SEROC IRQ raised\n");
+ m_irq_w_cb(ASSERT_LINE);
}
}
@@ -384,8 +413,8 @@ TIMER_CALLBACK_MEMBER(pokey_device::serin_ready_irq)
if (m_IRQEN & IRQ_SERIN)
{
m_IRQST |= IRQ_SERIN;
- if (!m_irq_f.isnull())
- m_irq_f(IRQ_SERIN);
+ LOG_IRQ("POKEY SERIN IRQ raised\n");
+ m_irq_w_cb(ASSERT_LINE);
}
}
@@ -404,7 +433,12 @@ TIMER_CALLBACK_MEMBER(pokey_device::sync_pot)
TIMER_CALLBACK_MEMBER(pokey_device::sync_set_irqst)
{
- m_IRQST |= (param & 0xff);
+ if (m_IRQEN & param)
+ {
+ LOG_IRQ("POKEY TIMR%d IRQ raised\n", param);
+ m_IRQST |= (param & 0xff);
+ m_irq_w_cb(ASSERT_LINE);
+ }
}
void pokey_device::execute_run()
@@ -439,9 +473,9 @@ void pokey_device::step_keyboard()
/* check if the break IRQ is enabled */
if (m_IRQEN & IRQ_BREAK)
{
+ LOG_IRQ("POKEY BREAK IRQ raised\n");
m_IRQST |= IRQ_BREAK;
- if (!m_irq_f.isnull())
- m_irq_f(IRQ_BREAK);
+ m_irq_w_cb(ASSERT_LINE);
}
}
break;
@@ -467,20 +501,20 @@ void pokey_device::step_keyboard()
}
break;
case 1: /* waiting for key confirmation */
- if ((m_kbd_latch & 0x3f) == m_kbd_cnt)
+ if (!(m_SKCTL & SK_DEBOUNCE) || (m_kbd_latch & 0x3f) == m_kbd_cnt)
{
if (ret & 1)
{
- m_KBCODE = m_kbd_latch;
+ m_KBCODE = (m_SKCTL & SK_DEBOUNCE) ? m_kbd_latch : (m_kbd_latch & 0xc0) | m_kbd_cnt;
m_SKSTAT |= SK_KEYBD;
if (m_IRQEN & IRQ_KEYBD)
{
/* last interrupt not acknowledged ? */
if (m_IRQST & IRQ_KEYBD)
m_SKSTAT |= SK_KBERR;
+ LOG_IRQ("POKEY KEYBD IRQ raised\n");
m_IRQST |= IRQ_KEYBD;
- if (!m_irq_f.isnull())
- m_irq_f(IRQ_KEYBD);
+ m_irq_w_cb(ASSERT_LINE);
}
m_kbd_state++;
}
@@ -489,16 +523,14 @@ void pokey_device::step_keyboard()
}
break;
case 2: /* waiting for release */
- if ((m_kbd_latch & 0x3f) == m_kbd_cnt)
+ if (!(m_SKCTL & SK_DEBOUNCE) || (m_kbd_latch & 0x3f) == m_kbd_cnt)
{
if ((ret & 1)==0)
m_kbd_state++;
- else
- m_SKSTAT |= SK_KEYBD;
}
break;
case 3:
- if ((m_kbd_latch & 0x3f) == m_kbd_cnt)
+ if (!(m_SKCTL & SK_DEBOUNCE) || (m_kbd_latch & 0x3f) == m_kbd_cnt)
{
if (ret & 1)
m_kbd_state = 2;
@@ -538,11 +570,12 @@ void pokey_device::step_pot()
*
*/
-void pokey_device::step_one_clock(void)
+void pokey_device::step_one_clock()
{
- /* Clocks only count if we are not in a reset */
if (m_SKCTL & SK_RESET)
{
+ /* Clocks only count if we are not in a reset */
+
/* polynom pointers */
if (++m_p4 == 0x0000f)
m_p4 = 0;
@@ -568,21 +601,36 @@ void pokey_device::step_one_clock(void)
clock_triggered[CLK_114] = 1;
}
- int const base_clock = (m_AUDCTL & CLK_15KHZ) ? CLK_114 : CLK_28;
- int clk = (m_AUDCTL & CH1_HICLK) ? CLK_1 : base_clock;
- if (clock_triggered[clk])
- m_channel[CHAN1].inc_chan();
+ if ((m_AUDCTL & CH1_HICLK) && (clock_triggered[CLK_1]))
+ {
+ if (m_AUDCTL & CH12_JOINED)
+ m_channel[CHAN1].inc_chan(*this, 7);
+ else
+ m_channel[CHAN1].inc_chan(*this, 4);
+ }
- clk = (m_AUDCTL & CH3_HICLK) ? CLK_1 : base_clock;
- if (clock_triggered[clk])
- m_channel[CHAN3].inc_chan();
+ int base_clock = (m_AUDCTL & CLK_15KHZ) ? CLK_114 : CLK_28;
+
+ if ((!(m_AUDCTL & CH1_HICLK)) && (clock_triggered[base_clock]))
+ m_channel[CHAN1].inc_chan(*this, 1);
+
+ if ((m_AUDCTL & CH3_HICLK) && (clock_triggered[CLK_1]))
+ {
+ if (m_AUDCTL & CH34_JOINED)
+ m_channel[CHAN3].inc_chan(*this, 7);
+ else
+ m_channel[CHAN3].inc_chan(*this, 4);
+ }
+
+ if ((!(m_AUDCTL & CH3_HICLK)) && (clock_triggered[base_clock]))
+ m_channel[CHAN3].inc_chan(*this, 1);
if (clock_triggered[base_clock])
{
if (!(m_AUDCTL & CH12_JOINED))
- m_channel[CHAN2].inc_chan();
+ m_channel[CHAN2].inc_chan(*this, 1);
if (!(m_AUDCTL & CH34_JOINED))
- m_channel[CHAN4].inc_chan();
+ m_channel[CHAN4].inc_chan(*this, 1);
}
/* Potentiometer handling */
@@ -594,38 +642,26 @@ void pokey_device::step_one_clock(void)
step_keyboard();
}
- /* do CHAN2 before CHAN1 because CHAN1 may set borrow! */
- if (m_channel[CHAN2].check_borrow())
+ if (m_channel[CHAN3].check_borrow())
{
- bool const isJoined(m_AUDCTL & CH12_JOINED);
- if (isJoined)
- m_channel[CHAN1].reset_channel();
- m_channel[CHAN2].reset_channel();
- process_channel(CHAN2);
-
- /* check if some of the requested timer interrupts are enabled */
- if ((m_IRQST & IRQ_TIMR2) && !m_irq_f.isnull())
- m_irq_f(IRQ_TIMR2);
- }
+ if (m_AUDCTL & CH34_JOINED)
+ m_channel[CHAN4].inc_chan(*this, 1);
+ else
+ m_channel[CHAN3].reset_channel();
- if (m_channel[CHAN1].check_borrow())
- {
- bool const isJoined(m_AUDCTL & CH12_JOINED);
- if (isJoined)
- m_channel[CHAN2].inc_chan();
+ process_channel(CHAN3);
+ /* is this a filtering channel (3/4) and is the filter active? */
+ if (m_AUDCTL & CH1_FILTER)
+ m_channel[CHAN1].sample();
else
- m_channel[CHAN1].reset_channel();
- process_channel(CHAN1);
- /* check if some of the requested timer interrupts are enabled */
- if ((m_IRQST & IRQ_TIMR1) && !m_irq_f.isnull())
- m_irq_f(IRQ_TIMR1);
+ m_channel[CHAN1].m_filter_sample = 1;
+
+ m_old_raw_inval = true;
}
- /* do CHAN4 before CHAN3 because CHAN3 may set borrow! */
if (m_channel[CHAN4].check_borrow())
{
- bool const isJoined(m_AUDCTL & CH34_JOINED);
- if (isJoined)
+ if (m_AUDCTL & CH34_JOINED)
m_channel[CHAN3].reset_channel();
m_channel[CHAN4].reset_channel();
process_channel(CHAN4);
@@ -634,23 +670,36 @@ void pokey_device::step_one_clock(void)
m_channel[CHAN2].sample();
else
m_channel[CHAN2].m_filter_sample = 1;
- if ((m_IRQST & IRQ_TIMR4) && !m_irq_f.isnull())
- m_irq_f(IRQ_TIMR4);
+
+ m_old_raw_inval = true;
}
- if (m_channel[CHAN3].check_borrow())
+ if ((m_SKCTL & SK_TWOTONE) && (m_channel[CHAN2].m_borrow_cnt == 1))
{
- bool const isJoined(m_AUDCTL & CH34_JOINED);
- if (isJoined)
- m_channel[CHAN4].inc_chan();
- else
- m_channel[CHAN3].reset_channel();
- process_channel(CHAN3);
- /* is this a filtering channel (3/4) and is the filter active? */
- if (m_AUDCTL & CH1_FILTER)
- m_channel[CHAN1].sample();
+ m_channel[CHAN1].reset_channel();
+ m_old_raw_inval = true;
+ }
+
+ if (m_channel[CHAN1].check_borrow())
+ {
+ if (m_AUDCTL & CH12_JOINED)
+ m_channel[CHAN2].inc_chan(*this, 1);
else
- m_channel[CHAN1].m_filter_sample = 1;
+ m_channel[CHAN1].reset_channel();
+
+ // TODO: If two-tone is enabled *and* serial output == 1 then reset the channel 2 timer.
+
+ process_channel(CHAN1);
+ }
+
+ if (m_channel[CHAN2].check_borrow())
+ {
+ if (m_AUDCTL & CH12_JOINED)
+ m_channel[CHAN1].reset_channel();
+
+ m_channel[CHAN2].reset_channel();
+
+ process_channel(CHAN2);
}
if (m_old_raw_inval)
@@ -663,10 +712,8 @@ void pokey_device::step_one_clock(void)
}
if (m_out_raw != sum)
- {
- //printf("forced update %08d %08x\n", m_icount, m_out_raw);
m_stream->update();
- }
+
m_old_raw_inval = false;
m_out_raw = sum;
}
@@ -762,12 +809,12 @@ uint8_t pokey_device::read(offs_t offset)
{
/* we have a value measured */
data = m_POTx[pot];
- LOG(("POKEY '%s' read POT%d (final value) $%02x\n", tag(), pot, data));
+ LOG("%s: POKEY read POT%d (final value) $%02x\n", machine().describe_context(), pot, data);
}
else
{
data = m_pot_counter;
- LOG(("POKEY '%s' read POT%d (interpolated) $%02x\n", tag(), pot, data));
+ LOG("%s: POKEY read POT%d (interpolated) $%02x\n", machine().describe_context(), pot, data);
}
break;
@@ -779,17 +826,17 @@ uint8_t pokey_device::read(offs_t offset)
if ((m_SKCTL & SK_RESET) == 0)
{
data = m_ALLPOT;
- LOG(("POKEY '%s' ALLPOT internal $%02x (reset)\n", tag(), data));
+ LOG("%s: POKEY ALLPOT internal $%02x (reset)\n", machine().describe_context(), data);
}
- else if (!m_allpot_r_cb.isnull())
+ else if (!m_allpot_r_cb.isunset())
{
m_ALLPOT = data = m_allpot_r_cb(offset);
- LOG(("%s: POKEY '%s' ALLPOT callback $%02x\n", machine().describe_context(), tag(), data));
+ LOG("%s: POKEY ALLPOT callback $%02x\n", machine().describe_context(), data);
}
else
{
data = m_ALLPOT ^ 0xff;
- LOG(("POKEY '%s' ALLPOT internal $%02x\n", tag(), data));
+ LOG("%s: POKEY ALLPOT internal $%02x\n", machine().describe_context(), data);
}
break;
@@ -801,37 +848,37 @@ uint8_t pokey_device::read(offs_t offset)
if (m_AUDCTL & POLY9)
{
data = m_poly9[m_p9] & 0xff;
- LOG_RAND(("POKEY '%s' rand9[$%05x]: $%02x\n", tag(), m_p9, data));
+ LOG_RAND("%s: POKEY rand9[$%05x]: $%02x\n", machine().describe_context(), m_p9, data);
}
else
{
data = (m_poly17[m_p17] >> 8) & 0xff;
- LOG_RAND(("POKEY '%s' rand17[$%05x]: $%02x\n", tag(), m_p17, data));
+ LOG_RAND("%s: POKEY rand17[$%05x]: $%02x\n", machine().describe_context(), m_p17, data);
}
break;
case SERIN_C:
- if (!m_serin_r_cb.isnull())
+ if (!m_serin_r_cb.isunset())
m_SERIN = m_serin_r_cb(offset);
data = m_SERIN;
- LOG(("POKEY '%s' SERIN $%02x\n", tag(), data));
+ LOG("%s: POKEY SERIN $%02x\n", machine().describe_context(), data);
break;
case IRQST_C:
/* IRQST is an active low input port; we keep it active high */
/* internally to ease the (un-)masking of bits */
data = m_IRQST ^ 0xff;
- LOG(("POKEY '%s' IRQST $%02x\n", tag(), data));
+ LOG("%s: POKEY IRQST $%02x\n", machine().describe_context(), data);
break;
case SKSTAT_C:
/* SKSTAT is also an active low input port */
data = m_SKSTAT ^ 0xff;
- LOG(("POKEY '%s' SKSTAT $%02x\n", tag(), data));
+ LOG("%s: POKEY SKSTAT $%02x\n", machine().describe_context(), data);
break;
default:
- LOG(("POKEY '%s' register $%02x\n", tag(), offset));
+ LOG("%s: POKEY register $%02x\n", machine().describe_context(), offset);
data = 0xff;
break;
}
@@ -854,59 +901,59 @@ void pokey_device::write_internal(offs_t offset, uint8_t data)
switch (offset & 15)
{
case AUDF1_C:
- LOG_SOUND(("POKEY '%s' AUDF1 $%02x\n", tag(), data));
+ LOG_SOUND("%s: AUDF1 = $%02x\n", machine().describe_context(), data);
m_channel[CHAN1].m_AUDF = data;
break;
case AUDC1_C:
- LOG_SOUND(("POKEY '%s' AUDC1 $%02x (%s)\n", tag(), data, audc2str(data)));
+ LOG_SOUND("%s: POKEY AUDC1 $%02x (%s)\n", machine().describe_context(), data, audc2str(data));
m_channel[CHAN1].m_AUDC = data;
m_old_raw_inval = true;
break;
case AUDF2_C:
- LOG_SOUND(("POKEY '%s' AUDF2 $%02x\n", tag(), data));
+ LOG_SOUND("%s: POKEY AUDF2 $%02x\n", machine().describe_context(), data);
m_channel[CHAN2].m_AUDF = data;
break;
case AUDC2_C:
- LOG_SOUND(("POKEY '%s' AUDC2 $%02x (%s)\n", tag(), data, audc2str(data)));
+ LOG_SOUND("%s: POKEY AUDC2 $%02x (%s)\n", machine().describe_context(), data, audc2str(data));
m_channel[CHAN2].m_AUDC = data;
m_old_raw_inval = true;
break;
case AUDF3_C:
- LOG_SOUND(("POKEY '%s' AUDF3 $%02x\n", tag(), data));
+ LOG_SOUND("%s: POKEY AUDF3 $%02x\n", machine().describe_context(), data);
m_channel[CHAN3].m_AUDF = data;
break;
case AUDC3_C:
- LOG_SOUND(("POKEY '%s' AUDC3 $%02x (%s)\n", tag(), data, audc2str(data)));
+ LOG_SOUND("%s: POKEY AUDC3 $%02x (%s)\n", machine().describe_context(), data, audc2str(data));
m_channel[CHAN3].m_AUDC = data;
m_old_raw_inval = true;
break;
case AUDF4_C:
- LOG_SOUND(("POKEY '%s' AUDF4 $%02x\n", tag(), data));
+ LOG_SOUND("%s: POKEY AUDF4 $%02x\n", machine().describe_context(), data);
m_channel[CHAN4].m_AUDF = data;
break;
case AUDC4_C:
- LOG_SOUND(("POKEY '%s' AUDC4 $%02x (%s)\n", tag(), data, audc2str(data)));
+ LOG_SOUND("%s: POKEY AUDC4 $%02x (%s)\n", machine().describe_context(), data, audc2str(data));
m_channel[CHAN4].m_AUDC = data;
m_old_raw_inval = true;
break;
case AUDCTL_C:
- if( data == m_AUDCTL )
+ if (data == m_AUDCTL)
return;
- LOG_SOUND(("POKEY '%s' AUDCTL $%02x (%s)\n", tag(), data, audctl2str(data)));
+ LOG_SOUND("%s: POKEY AUDCTL $%02x (%s)\n", machine().describe_context(), data, audctl2str(data));
m_AUDCTL = data;
-
+ m_old_raw_inval = true;
break;
case STIMER_C:
- LOG_TIMER(("POKEY '%s' STIMER $%02x\n", tag(), data));
+ LOG_TIMER("%s: POKEY STIMER $%02x\n", machine().describe_context(), data);
/* From the pokey documentation:
* reset all counters to zero (side effect)
@@ -924,17 +971,23 @@ void pokey_device::write_internal(offs_t offset, uint8_t data)
case SKREST_C:
/* reset SKSTAT */
- LOG(("POKEY '%s' SKREST $%02x\n", tag(), data));
+ LOG("%s: POKEY SKREST $%02x\n", machine().describe_context(), data);
m_SKSTAT &= ~(SK_FRAME|SK_OVERRUN|SK_KBERR);
break;
case POTGO_C:
- LOG(("POKEY '%s' POTGO $%02x\n", tag(), data));
- pokey_potgo();
+ LOG("%s: POKEY POTGO $%02x\n", machine().describe_context(), data);
+ if (m_SKCTL & SK_RESET)
+ pokey_potgo();
break;
case SEROUT_C:
- LOG(("POKEY '%s' SEROUT $%02x\n", tag(), data));
+ LOG("%s: POKEY SEROUT $%02x\n", machine().describe_context(), data);
+ // TODO: convert to real serial comms, fix timings
+ // SEROC (1) serial out in progress (0) serial out complete
+ // in progress status is necessary for a800 telelnk2 to boot
+ m_IRQST &= ~IRQ_SEROC;
+
m_serout_w_cb(offset, data);
m_SKSTAT |= SK_SEROUT;
/*
@@ -948,10 +1001,10 @@ void pokey_device::write_internal(offs_t offset, uint8_t data)
break;
case IRQEN_C:
- LOG(("POKEY '%s' IRQEN $%02x\n", tag(), data));
+ LOG("%s: POKEY IRQEN $%02x\n", machine().describe_context(), data);
/* acknowledge one or more IRQST bits ? */
- if( m_IRQST & ~data )
+ if (m_IRQST & ~data)
{
/* reset IRQST bits that are masked now, except the SEROC bit (acid5200 pokey_seroc test) */
m_IRQST &= (IRQ_SEROC | data);
@@ -961,17 +1014,22 @@ void pokey_device::write_internal(offs_t offset, uint8_t data)
/* if SEROC irq is enabled trigger an irq (acid5200 pokey_seroc test) */
if (m_IRQEN & m_IRQST & IRQ_SEROC)
{
- if (!m_irq_f.isnull())
- m_irq_f(IRQ_SEROC);
+ LOG_IRQ("POKEY SEROC IRQ enabled\n");
+ m_irq_w_cb(ASSERT_LINE);
+ }
+ else if (!(m_IRQEN & m_IRQST))
+ {
+ LOG_IRQ("POKEY IRQs all cleared\n");
+ m_irq_w_cb(CLEAR_LINE);
}
break;
case SKCTL_C:
- if( data == m_SKCTL )
+ if (data == m_SKCTL)
return;
- LOG(("POKEY '%s' SKCTL $%02x\n", tag(), data));
+ LOG("%s: POKEY SKCTL $%02x\n", machine().describe_context(), data);
m_SKCTL = data;
- if( !(data & SK_RESET) )
+ if (!(data & SK_RESET))
{
write_internal(IRQEN_C, 0);
write_internal(SKREST_C, 0);
@@ -989,9 +1047,15 @@ void pokey_device::write_internal(offs_t offset, uint8_t data)
m_clock_cnt[0] = 0;
m_clock_cnt[1] = 0;
m_clock_cnt[2] = 0;
- m_old_raw_inval = true;
/* FIXME: Serial port reset ! */
}
+ if (!(data & SK_KEYSCAN))
+ {
+ m_SKSTAT &= ~SK_KEYBD;
+ m_kbd_cnt = 0;
+ m_kbd_state = 0;
+ }
+ m_old_raw_inval = true;
break;
}
@@ -1005,7 +1069,7 @@ void pokey_device::write_internal(offs_t offset, uint8_t data)
}
-WRITE_LINE_MEMBER( pokey_device::sid_w )
+void pokey_device::sid_w(int state)
{
if (state)
{
@@ -1043,30 +1107,24 @@ inline void pokey_device::process_channel(int ch)
}
-void pokey_device::pokey_potgo(void)
+void pokey_device::pokey_potgo()
{
- int pot;
-
- if( (m_SKCTL & SK_RESET) == 0)
- return;
-
- LOG(("POKEY #%p pokey_potgo\n", (void *) this));
+ LOG("pokey_potgo\n");
m_ALLPOT = 0x00;
m_pot_counter = 0;
- for( pot = 0; pot < 8; pot++ )
+ for (int pot = 0; pot < 8; pot++)
{
m_POTx[pot] = 228;
- if( !m_pot_r_cb[pot].isnull() )
+ if (!m_pot_r_cb[pot].isunset())
{
int r = m_pot_r_cb[pot](pot);
- LOG(("POKEY %s pot_r(%d) returned $%02x\n", tag(), pot, r));
+ LOG("POKEY pot_r(%d) returned $%02x\n", pot, r);
if (r >= 228)
- {
r = 228;
- }
+
if (r == 0)
{
/* immediately set the ready - bit of m_ALLPOT
@@ -1104,12 +1162,12 @@ void pokey_device::vol_init()
}
r_chan[j] = 1.0 / rTot;
}
- if (VERBOSE)
+ if (VERBOSE & LOG_GENERAL)
for (int j=0; j<16; j++)
{
rTot = 1.0 / r_chan[j] + 3.0 / r_chan[0];
rTot = 1.0 / rTot;
- LOG(("%s: %3d - %4.3f\n", tag(), j, rTot / (rTot+pull_up)*4.75));
+ LOG("%3d - %4.3f\n", j, rTot / (rTot+pull_up)*4.75);
}
for (int j=0; j<0x10000; j++)
{
@@ -1124,58 +1182,54 @@ void pokey_device::vol_init()
}
-void pokey_device::poly_init_4_5(uint32_t *poly, int size, int xorbit, int invert)
+void pokey_device::poly_init_4_5(uint32_t *poly, int size)
{
+ LOG_POLY("poly %d\n", size);
+
int mask = (1 << size) - 1;
- int i;
uint32_t lfsr = 0;
- LOG_POLY(("poly %d\n", size));
- for( i = 0; i < mask; i++ )
+ int const xorbit = size - 1;
+ for (int i = 0; i < mask; i++)
{
- /* calculate next bit */
- int in = !((lfsr >> 0) & 1) ^ ((lfsr >> xorbit) & 1);
- lfsr = lfsr >> 1;
- lfsr = (in << (size-1)) | lfsr;
- *poly = lfsr ^ invert;
- LOG_POLY(("%05x: %02x\n", i, *poly));
+ lfsr = (lfsr << 1) | (~((lfsr >> 2) ^ (lfsr >> xorbit)) & 1);
+ *poly = lfsr & mask;
poly++;
}
}
void pokey_device::poly_init_9_17(uint32_t *poly, int size)
{
- int mask = (1 << size) - 1;
- int i;
- uint32_t lfsr =mask;
+ LOG_RAND("rand %d\n", size);
- LOG_RAND(("rand %d\n", size));
+ const uint32_t mask = util::make_bitmask<uint32_t>(size);
+ uint32_t lfsr = mask;
if (size == 17)
{
- for( i = 0; i < mask; i++ )
+ for (uint32_t i = 0; i < mask; i++)
{
- /* calculate next bit @ 7 */
- int in8 = ((lfsr >> 8) & 1) ^ ((lfsr >> 13) & 1);
- int in = (lfsr & 1);
+ // calculate next bit @ 7
+ const uint32_t in8 = BIT(lfsr, 8) ^ BIT(lfsr, 13);
+ const uint32_t in = BIT(lfsr, 0);
lfsr = lfsr >> 1;
lfsr = (lfsr & 0xff7f) | (in8 << 7);
lfsr = (in << 16) | lfsr;
*poly = lfsr;
- LOG_RAND(("%05x: %02x\n", i, *poly));
+ LOG_RAND("%05x: %02x\n", i, *poly);
poly++;
}
}
- else
+ else // size == 9
{
- for( i = 0; i < mask; i++ )
+ for (uint32_t i = 0; i < mask; i++)
{
- /* calculate next bit */
- int in = ((lfsr >> 0) & 1) ^ ((lfsr >> 5) & 1);
+ // calculate next bit
+ const uint32_t in = BIT(lfsr, 0) ^ BIT(lfsr, 5);
lfsr = lfsr >> 1;
lfsr = (in << 8) | lfsr;
*poly = lfsr;
- LOG_RAND(("%05x: %02x\n", i, *poly));
+ LOG_RAND("%05x: %02x\n", i, *poly);
poly++;
}
}
@@ -1185,22 +1239,20 @@ void pokey_device::poly_init_9_17(uint32_t *poly, int size)
char *pokey_device::audc2str(int val)
{
static char buff[80];
- if( val & NOTPOLY5 )
+ if (val & NOTPOLY5)
{
- if( val & PURE )
+ if (val & PURE)
strcpy(buff,"pure");
- else
- if( val & POLY4 )
+ else if (val & POLY4)
strcpy(buff,"poly4");
else
strcpy(buff,"poly9/17");
}
else
{
- if( val & PURE )
+ if (val & PURE)
strcpy(buff,"poly5");
- else
- if( val & POLY4 )
+ else if (val & POLY4)
strcpy(buff,"poly4+poly5");
else
strcpy(buff,"poly9/17+poly5");
@@ -1211,33 +1263,33 @@ char *pokey_device::audc2str(int val)
char *pokey_device::audctl2str(int val)
{
static char buff[80];
- if( val & POLY9 )
+ if (val & POLY9)
strcpy(buff,"poly9");
else
strcpy(buff,"poly17");
- if( val & CH1_HICLK )
+ if (val & CH1_HICLK)
strcat(buff,"+ch1hi");
- if( val & CH3_HICLK )
+ if (val & CH3_HICLK)
strcat(buff,"+ch3hi");
- if( val & CH12_JOINED )
+ if (val & CH12_JOINED)
strcat(buff,"+ch1/2");
- if( val & CH34_JOINED )
+ if (val & CH34_JOINED)
strcat(buff,"+ch3/4");
- if( val & CH1_FILTER )
+ if (val & CH1_FILTER)
strcat(buff,"+ch1filter");
- if( val & CH2_FILTER )
+ if (val & CH2_FILTER)
strcat(buff,"+ch2filter");
- if( val & CLK_15KHZ )
+ if (val & CLK_15KHZ)
strcat(buff,"+clk15");
return buff;
}
-pokey_device::pokey_channel::pokey_channel()
- : m_AUDF(0),
- m_AUDC(0),
- m_borrow_cnt(0),
- m_counter(0),
- m_output(0),
- m_filter_sample(0)
+pokey_device::pokey_channel::pokey_channel() :
+ m_AUDF(0),
+ m_AUDC(0),
+ m_borrow_cnt(0),
+ m_counter(0),
+ m_output(0),
+ m_filter_sample(0)
{
}
diff --git a/src/devices/sound/pokey.h b/src/devices/sound/pokey.h
index 1a111646df1..3d60bcfcf8f 100644
--- a/src/devices/sound/pokey.h
+++ b/src/devices/sound/pokey.h
@@ -2,12 +2,13 @@
// copyright-holders:Brad Oliver, Eric Smith, Juergen Buchmueller
/*****************************************************************************
*
- * POKEY chip emulator 4.6
+ * POKEY chip emulator 4.9
*
* Based on original info found in Ron Fries' Pokey emulator,
* with additions by Brad Oliver, Eric Smith and Juergen Buchmueller.
* paddle (a/d conversion) details from the Atari 400/800 Hardware Manual.
* Polynomial algorithms according to info supplied by Perry McFarlane.
+ * Additional improvements from Mike Saarna's A7800 MAME fork.
*
*****************************************************************************/
@@ -49,7 +50,6 @@
//**************************************************************************
#define POKEY_KEYBOARD_CB_MEMBER(_name) uint8_t _name(uint8_t k543210)
-#define POKEY_INTERRUPT_CB_MEMBER(_name) void _name(int mask)
//**************************************************************************
// TYPE DEFINITIONS
@@ -130,19 +130,17 @@ public:
auto allpot_r() { return m_allpot_r_cb.bind(); }
auto serin_r() { return m_serin_r_cb.bind(); }
auto serout_w() { return m_serout_w_cb.bind(); }
+ auto irq_w() { return m_irq_w_cb.bind(); }
/* k543210 = k5 ... k0 returns bit0: kr1, bit1: kr2 */
/* all are, in contrast to actual hardware, ACTIVE_HIGH */
typedef device_delegate<uint8_t (uint8_t k543210)> kb_cb_delegate;
template <typename... T> void set_keyboard_callback(T &&... args) { m_keyboard_r.set(std::forward<T>(args)...); }
- typedef device_delegate<void (int mask)> int_cb_delegate;
- template <typename... T> void set_interrupt_callback(T &&... args) { m_irq_f.set(std::forward<T>(args)...); }
-
uint8_t read(offs_t offset);
void write(offs_t offset, uint8_t data);
- DECLARE_WRITE_LINE_MEMBER( sid_w ); // pin 24
+ void sid_w(int state); // pin 24
void serin_ready(int after);
// analog output configuration
@@ -205,33 +203,33 @@ private:
{
public:
pokey_channel();
- pokey_device *m_parent;
+
uint8_t m_INTMask;
- uint8_t m_AUDF; /* AUDFx (D200, D202, D204, D206) */
- uint8_t m_AUDC; /* AUDCx (D201, D203, D205, D207) */
- int32_t m_borrow_cnt; /* borrow counter */
- int32_t m_counter; /* channel counter */
- uint8_t m_output; /* channel output signal (1 active, 0 inactive) */
- uint8_t m_filter_sample; /* high-pass filter sample */
+ uint8_t m_AUDF; // AUDFx (D200, D202, D204, D206)
+ uint8_t m_AUDC; // AUDCx (D201, D203, D205, D207)
+ int32_t m_borrow_cnt; // borrow counter
+ int32_t m_counter; // channel counter
+ uint8_t m_output; // channel output signal (1 active, 0 inactive)
+ uint8_t m_filter_sample; // high-pass filter sample
- inline void sample(void) { m_filter_sample = m_output; }
- inline void reset_channel(void) { m_counter = m_AUDF ^ 0xff; }
+ void sample() { m_filter_sample = m_output; }
+ void reset_channel() { m_counter = m_AUDF ^ 0xff; m_borrow_cnt = 0; }
- inline void inc_chan()
+ void inc_chan(pokey_device &host, int cycles)
{
m_counter = (m_counter + 1) & 0xff;
if (m_counter == 0 && m_borrow_cnt == 0)
{
- m_borrow_cnt = 3;
- if (m_parent->m_IRQEN & m_INTMask)
+ m_borrow_cnt = cycles;
+ if (host.m_IRQEN & m_INTMask)
{
/* Exposed state has changed: This should only be updated after a resync ... */
- m_parent->machine().scheduler().synchronize(timer_expired_delegate(FUNC(pokey_device::sync_set_irqst), m_parent), m_INTMask);
+ host.machine().scheduler().synchronize(timer_expired_delegate(FUNC(pokey_device::sync_set_irqst), &host), m_INTMask);
}
}
}
- inline int check_borrow()
+ int check_borrow()
{
if (m_borrow_cnt > 0)
{
@@ -248,12 +246,12 @@ private:
void step_keyboard();
void step_pot();
- void poly_init_4_5(uint32_t *poly, int size, int xorbit, int invert);
+ void poly_init_4_5(uint32_t *poly, int size);
void poly_init_9_17(uint32_t *poly, int size);
void vol_init();
inline void process_channel(int ch);
- void pokey_potgo(void);
+ void pokey_potgo();
char *audc2str(int val);
char *audctl2str(int val);
@@ -278,9 +276,9 @@ private:
devcb_read8 m_allpot_r_cb;
devcb_read8 m_serin_r_cb;
devcb_write8 m_serout_w_cb;
+ devcb_write_line m_irq_w_cb;
kb_cb_delegate m_keyboard_r;
- int_cb_delegate m_irq_f;
uint8_t m_POTx[8]; /* POTx (R/D200-D207) */
uint8_t m_AUDCTL; /* AUDCTL (W/D208) */
diff --git a/src/devices/sound/qs1000.cpp b/src/devices/sound/qs1000.cpp
index ff45ec51093..b3f494890b4 100644
--- a/src/devices/sound/qs1000.cpp
+++ b/src/devices/sound/qs1000.cpp
@@ -164,20 +164,19 @@ ROM_END
//-------------------------------------------------
// qs1000_device - constructor
//-------------------------------------------------
-qs1000_device::qs1000_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, QS1000, tag, owner, clock),
- device_sound_interface(mconfig, *this),
- device_rom_interface(mconfig, *this),
- m_external_rom(false),
- m_in_p1_cb(*this),
- m_in_p2_cb(*this),
- m_in_p3_cb(*this),
- m_out_p1_cb(*this),
- m_out_p2_cb(*this),
- m_out_p3_cb(*this),
- //m_serial_w_cb(*this),
- m_stream(nullptr),
- m_cpu(*this, "cpu")
+qs1000_device::qs1000_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, QS1000, tag, owner, clock),
+ device_sound_interface(mconfig, *this),
+ device_rom_interface(mconfig, *this),
+ m_external_rom(false),
+ m_in_p1_cb(*this, 0),
+ m_in_p2_cb(*this, 0),
+ m_in_p3_cb(*this, 0),
+ m_out_p1_cb(*this),
+ m_out_p2_cb(*this),
+ m_out_p3_cb(*this),
+ m_stream(nullptr),
+ m_cpu(*this, "cpu")
{
}
@@ -207,15 +206,15 @@ void qs1000_device::device_add_mconfig(machine_config &config)
m_cpu->port_out_cb<2>().set(FUNC(qs1000_device::p2_w));
m_cpu->port_in_cb<3>().set(FUNC(qs1000_device::p3_r));
m_cpu->port_out_cb<3>().set(FUNC(qs1000_device::p3_w));
- m_cpu->serial_rx_cb().set(FUNC(qs1000_device::data_to_i8052));
}
//-------------------------------------------------
-// rom_bank_updated - the rom bank has changed
+// rom_bank_pre_change - refresh the stream if the
+// ROM banking changes
//-------------------------------------------------
-void qs1000_device::rom_bank_updated()
+void qs1000_device::rom_bank_pre_change()
{
m_stream->update();
}
@@ -231,18 +230,6 @@ void qs1000_device::device_start()
// gives reasonable results
m_stream = stream_alloc(0, 2, clock() / 32);
- // Resolve CPU port callbacks
- m_in_p1_cb.resolve_safe(0);
- m_in_p2_cb.resolve_safe(0);
- m_in_p3_cb.resolve_safe(0);
-
- m_out_p1_cb.resolve_safe();
- m_out_p2_cb.resolve_safe();
- m_out_p3_cb.resolve_safe();
-
- //m_serial_w_cb.resolve_safe();
-
- save_item(NAME(m_serial_data_in));
save_item(NAME(m_wave_regs));
for (int i = 0; i < QS1000_CHANNELS; i++)
@@ -267,19 +254,6 @@ void qs1000_device::device_start()
//-------------------------------------------------
-// serial_in - send data to the chip
-//-------------------------------------------------
-void qs1000_device::serial_in(uint8_t data)
-{
- m_serial_data_in = data;
-
- // Signal to the CPU that data is available
- m_cpu->set_input_line(MCS51_RX_LINE, ASSERT_LINE);
- m_cpu->set_input_line(MCS51_RX_LINE, CLEAR_LINE);
-}
-
-
-//-------------------------------------------------
// set_irq - interrupt the internal CPU
//-------------------------------------------------
void qs1000_device::set_irq(int state)
@@ -290,16 +264,6 @@ void qs1000_device::set_irq(int state)
//-------------------------------------------------
-// data_to_i8052 - called by the 8052 core to
-// receive serial data
-//-------------------------------------------------
-uint8_t qs1000_device::data_to_i8052()
-{
- return m_serial_data_in;
-}
-
-
-//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void qs1000_device::device_reset()
diff --git a/src/devices/sound/qs1000.h b/src/devices/sound/qs1000.h
index 2624d893739..f921c2fd947 100644
--- a/src/devices/sound/qs1000.h
+++ b/src/devices/sound/qs1000.h
@@ -40,11 +40,9 @@ public:
auto p1_out() { return m_out_p1_cb.bind(); }
auto p2_out() { return m_out_p2_cb.bind(); }
auto p3_out() { return m_out_p3_cb.bind(); }
- //auto serial_w() { return m_serial_w_cb.bind(); }
// external
i8052_device &cpu() const { return *m_cpu; }
- void serial_in(uint8_t data);
void set_irq(int state);
void wave_w(offs_t offset, uint8_t data);
@@ -75,7 +73,7 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
// device_rom_interface overrides
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
private:
static constexpr unsigned QS1000_CHANNELS = 32;
@@ -101,14 +99,11 @@ private:
devcb_write8 m_out_p2_cb;
devcb_write8 m_out_p3_cb;
- //devcb_write8 m_serial_w_cb;
-
// Internal state
sound_stream * m_stream;
required_device<i8052_device> m_cpu;
// Wavetable engine
- uint8_t m_serial_data_in;
uint8_t m_wave_regs[18];
struct qs1000_channel
@@ -129,8 +124,6 @@ private:
};
qs1000_channel m_channels[QS1000_CHANNELS];
-
- uint8_t data_to_i8052();
};
diff --git a/src/devices/sound/qsound.cpp b/src/devices/sound/qsound.cpp
index 123b19443b4..257fa135bb5 100644
--- a/src/devices/sound/qsound.cpp
+++ b/src/devices/sound/qsound.cpp
@@ -9,7 +9,7 @@
The key components are a DSP16A, a TDA1543 dual 16-bit DAC with I2S
input, and a TC9185P electronic volume control. The TDA1543 is
- simulated here; no attempt is being made to emulate theTC9185P.
+ simulated here; no attempt is being made to emulate the TC9185P.
Commands work by writing an address/data word pair to be written to
DSP's internal RAM. In theory it's possible to write anywhere in
@@ -26,7 +26,7 @@
is needed because DSP16 has latent PIO reads in active mode). I've
assumed that reading PIO with PSEL low when INT is asserted will
return the address and cause INT to be de-asserted, and reading PIO
- with PSEL low when int is not asserted will return the data word.
+ with PSEL low when INT is not asserted will return the data word.
The DSP program will only respond to one external interrupt per
sample interval (i.e. the maximum command rate is the same as the
sample rate).
@@ -102,7 +102,6 @@
#include <algorithm>
#include <fstream>
-#define LOG_GENERAL (1U << 0)
#define LOG_COMMAND (1U << 1)
#define LOG_SAMPLE (1U << 2)
@@ -262,10 +261,11 @@ void qsound_device::sound_stream_update(sound_stream &stream, std::vector<read_s
//-------------------------------------------------
-// rom_bank_updated - the rom bank has changed
+// rom_bank_post_change - called after the ROM
+// bank is changed
//-------------------------------------------------
-void qsound_device::rom_bank_updated()
+void qsound_device::rom_bank_post_change()
{
machine().scheduler().synchronize();
}
@@ -288,7 +288,7 @@ u16 qsound_device::dsp_sample_r(offs_t offset)
return u16(byte) << 8;
}
-WRITE_LINE_MEMBER(qsound_device::dsp_ock_w)
+void qsound_device::dsp_ock_w(int state)
{
// detect active edge
if (bool(state) == bool(m_ock))
diff --git a/src/devices/sound/qsound.h b/src/devices/sound/qsound.h
index a2886318275..427326157eb 100644
--- a/src/devices/sound/qsound.h
+++ b/src/devices/sound/qsound.h
@@ -35,7 +35,7 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
// device_rom_interface implementation
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_post_change() override;
void dsp_io_map(address_map &map);
@@ -45,7 +45,7 @@ private:
void dsp_pio_w(offs_t offset, u16 data);
// for synchronised DSP communication
- DECLARE_WRITE_LINE_MEMBER(dsp_ock_w);
+ void dsp_ock_w(int state);
u16 dsp_pio_r();
void set_dsp_ready(s32 param);
void set_cmd(s32 param);
diff --git a/src/devices/sound/qsoundhle.cpp b/src/devices/sound/qsoundhle.cpp
index a9595a4b00d..ed69b569cb5 100644
--- a/src/devices/sound/qsoundhle.cpp
+++ b/src/devices/sound/qsoundhle.cpp
@@ -50,10 +50,11 @@ qsound_hle_device::qsound_hle_device(const machine_config &mconfig, const char *
}
//-------------------------------------------------
-// rom_bank_updated - the rom bank has changed
+// rom_bank_pre_change - refresh the stream if the
+// ROM banking changes
//-------------------------------------------------
-void qsound_hle_device::rom_bank_updated()
+void qsound_hle_device::rom_bank_pre_change()
{
m_stream->update();
}
diff --git a/src/devices/sound/qsoundhle.h b/src/devices/sound/qsoundhle.h
index 4d6f61e0c7d..d5256854842 100644
--- a/src/devices/sound/qsoundhle.h
+++ b/src/devices/sound/qsoundhle.h
@@ -33,7 +33,7 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
// device_rom_interface implementation
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
private:
diff --git a/src/devices/sound/rf5c400.cpp b/src/devices/sound/rf5c400.cpp
index f15b7923260..fc466fb0a0f 100644
--- a/src/devices/sound/rf5c400.cpp
+++ b/src/devices/sound/rf5c400.cpp
@@ -349,7 +349,7 @@ void rf5c400_device::sound_stream_update(sound_stream &stream, std::vector<read_
}
}
-void rf5c400_device::rom_bank_updated()
+void rf5c400_device::rom_bank_pre_change()
{
m_stream->update();
}
diff --git a/src/devices/sound/rf5c400.h b/src/devices/sound/rf5c400.h
index 8f54d2bc8b3..a91ba9ee097 100644
--- a/src/devices/sound/rf5c400.h
+++ b/src/devices/sound/rf5c400.h
@@ -37,7 +37,7 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
// device_rom_interface overrides
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
private:
struct rf5c400_channel
diff --git a/src/devices/sound/rolandpcm.cpp b/src/devices/sound/rolandpcm.cpp
index 41f8f351e38..d1ec25c8f5c 100644
--- a/src/devices/sound/rolandpcm.cpp
+++ b/src/devices/sound/rolandpcm.cpp
@@ -39,17 +39,6 @@ mb87419_mb87420_device::mb87419_mb87420_device(const machine_config &mconfig, co
}
//-------------------------------------------------
-// device_resolve_objects - resolve objects that
-// may be needed for other devices to set
-// initial conditions at start time
-//-------------------------------------------------
-
-void mb87419_mb87420_device::device_resolve_objects()
-{
- m_int_callback.resolve_safe();
-}
-
-//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
@@ -73,10 +62,11 @@ void mb87419_mb87420_device::device_reset()
}
//-------------------------------------------------
-// rom_bank_updated - the rom bank has changed
+// rom_bank_pre_change - refresh the stream if the
+// ROM banking changes
//-------------------------------------------------
-void mb87419_mb87420_device::rom_bank_updated()
+void mb87419_mb87420_device::rom_bank_pre_change()
{
// unused right now
m_stream->update();
@@ -253,7 +243,7 @@ void mb87419_mb87420_device::write(offs_t offset, u8 data)
}
break;
case 0x1F:
- m_sel_chn = data;
+ m_sel_chn = data & 0x1F;
break;
default:
logerror("Writing unknown reg %02X = %02X\n", offset, data);
@@ -363,7 +353,7 @@ int16_t mb87419_mb87420_device::decode_sample(int8_t data)
int16_t val;
int16_t sign;
uint8_t shift;
- int16_t result;
+ int16_t result;
if (data < 0)
{
diff --git a/src/devices/sound/rolandpcm.h b/src/devices/sound/rolandpcm.h
index 1c77be39b98..c8f1ebb5c08 100644
--- a/src/devices/sound/rolandpcm.h
+++ b/src/devices/sound/rolandpcm.h
@@ -18,16 +18,15 @@ public:
void write(offs_t offset, u8 data);
protected:
- // device-level overrides
- virtual void device_resolve_objects() override;
+ // device_t implementation
virtual void device_start() override;
virtual void device_reset() override;
- // sound stream update overrides
+ // device_sound_interface implementation
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
- // device_rom_interface overrides
- virtual void rom_bank_updated() override;
+ // device_rom_interface implementation
+ virtual void rom_bank_pre_change() override;
static int16_t decode_sample(int8_t data);
static int16_t sample_interpolate(int16_t smp1, int16_t smp2, uint16_t frac);
@@ -47,6 +46,7 @@ private:
uint32_t start = 0; // start address (18.14 fixed point)
uint16_t end = 0; // end offset (high word)
uint16_t loop = 0; // loop offset (high word)
+
// work variables
bool enable = false;
int8_t play_dir = 0; // playing direction, -1 [backwards] / 0 [stopped] / +1 [forwards]
diff --git a/src/devices/sound/s14001a.cpp b/src/devices/sound/s14001a.cpp
index 50f2ca07eaa..e93ca984480 100644
--- a/src/devices/sound/s14001a.cpp
+++ b/src/devices/sound/s14001a.cpp
@@ -196,13 +196,13 @@ uint8_t CalculateOutput(bool bVoiced, bool bXSilence, uint8_t uPPQtr, bool bPPQS
// device definition
DEFINE_DEVICE_TYPE(S14001A, s14001a_device, "s14001a", "SSi TSI S14001A")
-s14001a_device::s14001a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, S14001A, tag, owner, clock),
- device_sound_interface(mconfig, *this),
- m_SpeechRom(*this, DEVICE_SELF),
- m_stream(nullptr),
- m_bsy_handler(*this),
- m_ext_read_handler(*this)
+s14001a_device::s14001a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, S14001A, tag, owner, clock),
+ device_sound_interface(mconfig, *this),
+ m_SpeechRom(*this, DEVICE_SELF),
+ m_stream(nullptr),
+ m_bsy_handler(*this),
+ m_ext_read_handler(*this, 0)
{
}
@@ -216,10 +216,6 @@ void s14001a_device::device_start()
{
m_stream = stream_alloc(0, 1, clock() ? clock() : machine().sample_rate());
- // resolve callbacks
- m_ext_read_handler.resolve();
- m_bsy_handler.resolve();
-
// zero-fill
m_bPhase1 = false;
m_uStateP1 = m_uStateP2 = states::IDLE;
@@ -361,7 +357,7 @@ void s14001a_device::set_clock(uint32_t clock)
uint8_t s14001a_device::readmem(uint16_t offset, bool phase)
{
offset &= 0xfff; // 11-bit internal
- return ((m_ext_read_handler.isnull()) ? m_SpeechRom[offset & (m_SpeechRom.bytes() - 1)] : m_ext_read_handler(offset));
+ return (m_ext_read_handler.isunset()) ? m_SpeechRom[offset & (m_SpeechRom.bytes() - 1)] : m_ext_read_handler(offset);
}
bool s14001a_device::Clock()
@@ -413,7 +409,7 @@ bool s14001a_device::Clock()
m_uOutputP1 = 7;
if (m_bStart) m_uStateP1 = states::WORDWAIT;
- if (m_bBusyP1 && !m_bsy_handler.isnull())
+ if (m_bBusyP1)
m_bsy_handler(0);
m_bBusyP1 = false;
break;
@@ -428,7 +424,7 @@ bool s14001a_device::Clock()
if (m_bStart) m_uStateP1 = states::WORDWAIT;
else m_uStateP1 = states::CWARMSB;
- if (!m_bBusyP1 && !m_bsy_handler.isnull())
+ if (!m_bBusyP1)
m_bsy_handler(1);
m_bBusyP1 = true;
break;
diff --git a/src/devices/sound/samples.cpp b/src/devices/sound/samples.cpp
index bd8fbb1144f..afd72f5391a 100644
--- a/src/devices/sound/samples.cpp
+++ b/src/devices/sound/samples.cpp
@@ -2,23 +2,21 @@
// copyright-holders:Aaron Giles
/***************************************************************************
- samples.c
-
Sound device for sample playback.
****************************************************************************
- Playback of pre-recorded samples. Used for high-level simulation of
- discrete sound circuits where proper low-level simulation isn't
- available. Also used for tape loops and similar.
-
- Current limitations
- - Only supports single channel samples!
+Playback of pre-recorded samples. Used for high-level simulation of discrete
+sound circuits where proper low-level simulation isn't available. Also used
+for tape loops and similar.
- Considerations
- - Maybe this should be part of the presentation layer
- (artwork etc.) with samples specified in .lay files instead of
- in drivers?
+TODO:
+- Only supports single channel samples!
+- When mame.ini samplerate is close to the loaded sample(s) samplerate,
+ (eg. 48000, with 44100Hz samples), things can sound quite bad. This is
+ more an issue in sound.cpp resampler, not this device.
+- Maybe this should be part of the presentation layer (artwork etc.)
+ with samples specified in .lay files instead of in drivers?
***************************************************************************/
@@ -56,9 +54,9 @@ samples_device::samples_device(const machine_config &mconfig, const char *tag, d
samples_device::samples_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, type, tag, owner, clock)
, device_sound_interface(mconfig, *this)
+ , m_samples_start_cb(*this)
, m_channels(0)
, m_names(nullptr)
- , m_samples_start_cb(*this)
{
}
diff --git a/src/devices/sound/samples.h b/src/devices/sound/samples.h
index e7c2e5660fa..c2123fe1dbb 100644
--- a/src/devices/sound/samples.h
+++ b/src/devices/sound/samples.h
@@ -2,8 +2,6 @@
// copyright-holders:Aaron Giles
/***************************************************************************
- samples.h
-
Sound device for sample playback.
***************************************************************************/
@@ -14,10 +12,6 @@
#pragma once
-//**************************************************************************
-// GLOBAL VARIABLES
-//**************************************************************************
-
// device type definition
DECLARE_DEVICE_TYPE(SAMPLES, samples_device)
@@ -48,6 +42,9 @@ public:
// getters
bool playing(uint8_t channel) const;
uint32_t base_frequency(uint8_t channel) const;
+ uint8_t channels() { return m_channels; }
+ size_t samples() { return m_sample.size(); }
+ const char *const *names() { return m_names; }
// start/stop helpers
void start(uint8_t channel, uint32_t samplenum, bool loop = false);
@@ -66,15 +63,11 @@ public:
// shouldn't need a copy, but in case it happens, catch it here
sample_t &operator=(const sample_t &rhs) { assert(false); return *this; }
- uint32_t frequency; // frequency of the sample
- std::vector<int16_t> data; // 16-bit signed data
+ uint32_t frequency; // frequency of the sample
+ std::vector<int16_t> data; // 16-bit signed data
};
static bool read_sample(emu_file &file, sample_t &sample);
- // interface
- uint8_t m_channels; // number of discrete audio channels needed
- const char *const *m_names; // array of sample names
-
protected:
// subclasses can do it this way
samples_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
@@ -90,8 +83,8 @@ protected:
// internal classes
struct channel_t
{
- sound_stream * stream;
- const int16_t * source;
+ sound_stream *stream;
+ const int16_t *source;
int32_t source_num;
uint32_t source_len;
double pos;
@@ -108,9 +101,13 @@ protected:
start_cb_delegate m_samples_start_cb; // optional callback
+ // interface
+ uint8_t m_channels; // number of discrete audio channels needed
+ const char *const *m_names; // array of sample names
+
// internal state
- std::vector<channel_t> m_channel;
- std::vector<sample_t> m_sample;
+ std::vector<channel_t> m_channel;
+ std::vector<sample_t> m_sample;
// internal constants
static constexpr uint8_t FRAC_BITS = 24;
@@ -135,24 +132,24 @@ public:
}
// getters
- const char *altbasename() const { return (m_samples.m_names && m_samples.m_names[0] && m_samples.m_names[0][0] == '*') ? &m_samples.m_names[0][1] : nullptr; }
+ const char *altbasename() const { return (m_samples.names() && m_samples.names()[0] && m_samples.names()[0][0] == '*') ? &m_samples.names()[0][1] : nullptr; }
// iteration
const char *first()
{
- if (!m_samples.m_names || !m_samples.m_names[0])
+ if (!m_samples.names() || !m_samples.names()[0])
return nullptr;
m_current = 0;
- if (m_samples.m_names[0][0] == '*')
+ if (m_samples.names()[0][0] == '*')
m_current++;
- return m_samples.m_names[m_current++];
+ return m_samples.names()[m_current++];
}
const char *next()
{
- if (m_current == -1 || !m_samples.m_names[m_current])
+ if (m_current == -1 || !m_samples.names()[m_current])
return nullptr;
- return m_samples.m_names[m_current++];
+ return m_samples.names()[m_current++];
}
// counting
@@ -169,7 +166,7 @@ public:
private:
// internal state
samples_device &m_samples;
- int m_current;
+ int m_current;
};
#endif // MAME_SOUND_SAMPLES_H
diff --git a/src/devices/sound/scsp.cpp b/src/devices/sound/scsp.cpp
index f58907b8611..be8fc35cdc7 100644
--- a/src/devices/sound/scsp.cpp
+++ b/src/devices/sound/scsp.cpp
@@ -199,10 +199,6 @@ void scsp_device::device_start()
// init the emulation
init();
- // set up the IRQ callbacks
- m_irq_cb.resolve_safe();
- m_main_irq_cb.resolve_safe();
-
// Stereo output with EXTS0,1 Input (External digital audio output)
m_stream = stream_alloc(2, 2, clock() / 512);
@@ -303,7 +299,7 @@ void scsp_device::device_clock_changed()
m_stream->set_sample_rate(clock() / 512);
}
-void scsp_device::rom_bank_updated()
+void scsp_device::rom_bank_pre_change()
{
m_stream->update();
}
@@ -763,7 +759,7 @@ void scsp_device::UpdateReg(int reg)
break;
case 0x18:
case 0x19:
- if (!m_irq_cb.isnull())
+ if (!m_irq_cb.isunset())
{
m_TimPris[0] = 1 << ((m_udata.data[0x18/2] >> 8) & 0x7);
m_TimCnt[0] = (m_udata.data[0x18/2] & 0xff) << 8;
@@ -780,7 +776,7 @@ void scsp_device::UpdateReg(int reg)
break;
case 0x1a:
case 0x1b:
- if (!m_irq_cb.isnull())
+ if (!m_irq_cb.isunset())
{
m_TimPris[1] = 1 << ((m_udata.data[0x1A/2] >> 8) & 0x7);
m_TimCnt[1] = (m_udata.data[0x1A/2] & 0xff) << 8;
@@ -797,7 +793,7 @@ void scsp_device::UpdateReg(int reg)
break;
case 0x1C:
case 0x1D:
- if (!m_irq_cb.isnull())
+ if (!m_irq_cb.isunset())
{
m_TimPris[2] = 1 << ((m_udata.data[0x1C/2] >> 8) & 0x7);
m_TimCnt[2] = (m_udata.data[0x1C/2] & 0xff) << 8;
@@ -814,7 +810,7 @@ void scsp_device::UpdateReg(int reg)
break;
case 0x1e: // SCIEB
case 0x1f:
- if (!m_irq_cb.isnull())
+ if (!m_irq_cb.isunset())
{
CheckPendingIRQ();
@@ -824,7 +820,7 @@ void scsp_device::UpdateReg(int reg)
break;
case 0x20: // SCIPD
case 0x21:
- if (!m_irq_cb.isnull())
+ if (!m_irq_cb.isunset())
{
if (m_udata.data[0x1e/2] & m_udata.data[0x20/2] & 0x20)
popmessage("SCSP SCIPD write %04x, contact MAMEdev",m_udata.data[0x20/2]);
@@ -833,7 +829,7 @@ void scsp_device::UpdateReg(int reg)
case 0x22: //SCIRE
case 0x23:
- if (!m_irq_cb.isnull())
+ if (!m_irq_cb.isunset())
{
m_udata.data[0x20/2] &= ~m_udata.data[0x22/2];
ResetInterrupts();
@@ -860,7 +856,7 @@ void scsp_device::UpdateReg(int reg)
case 0x27:
case 0x28:
case 0x29:
- if (!m_irq_cb.isnull())
+ if (!m_irq_cb.isunset())
{
m_IrqTimA = DecodeSCI(SCITMA);
m_IrqTimBC = DecodeSCI(SCITMB);
diff --git a/src/devices/sound/scsp.h b/src/devices/sound/scsp.h
index f3d08df9793..dfd62c24dd0 100644
--- a/src/devices/sound/scsp.h
+++ b/src/devices/sound/scsp.h
@@ -44,7 +44,7 @@ protected:
virtual void device_post_load() override;
virtual void device_clock_changed() override;
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
// sound stream update overrides
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
diff --git a/src/devices/sound/segapcm.cpp b/src/devices/sound/segapcm.cpp
index 9b4d7a9c803..9f7a9b6fc63 100644
--- a/src/devices/sound/segapcm.cpp
+++ b/src/devices/sound/segapcm.cpp
@@ -58,10 +58,11 @@ void segapcm_device::device_clock_changed()
//-------------------------------------------------
-// rom_bank_updated - the rom bank has changed
+// rom_bank_pre_change - refresh the stream if the
+// ROM banking changes
//-------------------------------------------------
-void segapcm_device::rom_bank_updated()
+void segapcm_device::rom_bank_pre_change()
{
m_stream->update();
}
diff --git a/src/devices/sound/segapcm.h b/src/devices/sound/segapcm.h
index f9869f2211f..ff098088037 100644
--- a/src/devices/sound/segapcm.h
+++ b/src/devices/sound/segapcm.h
@@ -44,7 +44,7 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
// device_rom_interface overrides
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
private:
std::unique_ptr<uint8_t[]> m_ram;
diff --git a/src/devices/sound/setapcm.cpp b/src/devices/sound/setapcm.cpp
index 61346643216..ab002f68f10 100644
--- a/src/devices/sound/setapcm.cpp
+++ b/src/devices/sound/setapcm.cpp
@@ -162,11 +162,12 @@ void setapcm_device<MaxVoices, Divider>::sound_stream_update(sound_stream &strea
}
//-------------------------------------------------
-// rom_bank_updated - the rom bank has changed
+// rom_bank_pre_change - refresh the stream if the
+// ROM banking changes
//-------------------------------------------------
template<unsigned MaxVoices, unsigned Divider>
-void setapcm_device<MaxVoices, Divider>::rom_bank_updated()
+void setapcm_device<MaxVoices, Divider>::rom_bank_pre_change()
{
m_stream->update();
}
diff --git a/src/devices/sound/setapcm.h b/src/devices/sound/setapcm.h
index 8c406c7076f..2da5c147563 100644
--- a/src/devices/sound/setapcm.h
+++ b/src/devices/sound/setapcm.h
@@ -41,7 +41,7 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
// device_rom_interface implementation
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
static constexpr unsigned MAX_VOICES = MaxVoices; // max voices
static constexpr unsigned CLOCK_DIVIDER = Divider; // clock divider for generate output rate
diff --git a/src/devices/sound/sn76477.cpp b/src/devices/sound/sn76477.cpp
index 1ccbd2fc4b5..1ed2f8b57fe 100644
--- a/src/devices/sound/sn76477.cpp
+++ b/src/devices/sound/sn76477.cpp
@@ -49,7 +49,8 @@
*
*****************************************************************************/
-#define VERBOSE 1
+#define VERBOSE (0)
+#include "logmacro.h"
/* if 1, create a .wav file of the sound produced */
#define LOG_WAV 0
@@ -75,8 +76,6 @@
#define LOG_WAV_FILE_NAME "sn76477_%s.wav"
-#define LOG(n,...) do { if (VERBOSE >= (n)) logerror(__VA_ARGS__); } while (0)
-
#define CHECK_BOOLEAN assert((state & 0x01) == state)
#define CHECK_POSITIVE assert(data >= 0.0)
#define CHECK_VOLTAGE assert((data >= 0.0) && (data <= 5.0))
@@ -627,7 +626,7 @@ void sn76477_device::log_enable_line()
"Enabled", "Inhibited"
};
- LOG(1, "SN76477: Enable line (9): %d [%s]\n", m_enable, desc[m_enable]);
+ LOG("Enable line (9): %d [%s]\n", m_enable, desc[m_enable]);
}
@@ -639,7 +638,7 @@ void sn76477_device::log_mixer_mode()
"SLF/Noise", "SLF/VCO/Noise", "SLF/VCO", "Inhibit"
};
- LOG(1, "SN76477: Mixer mode (25-27): %d [%s]\n", m_mixer_mode, desc[m_mixer_mode]);
+ LOG("Mixer mode (25-27): %d [%s]\n", m_mixer_mode, desc[m_mixer_mode]);
}
@@ -650,7 +649,7 @@ void sn76477_device::log_envelope_mode()
"VCO", "One-Shot", "Mixer Only", "VCO with Alternating Polarity"
};
- LOG(1, "SN76477: Envelope mode (1,28): %d [%s]\n", m_envelope_mode, desc[m_envelope_mode]);
+ LOG("Envelope mode (1,28): %d [%s]\n", m_envelope_mode, desc[m_envelope_mode]);
}
@@ -661,7 +660,7 @@ void sn76477_device::log_vco_mode()
"External (Pin 16)", "Internal (SLF)"
};
- LOG(1, "SN76477: VCO mode (22): %d [%s]\n", m_vco_mode, desc[m_vco_mode]);
+ LOG("VCO mode (22): %d [%s]\n", m_vco_mode, desc[m_vco_mode]);
}
@@ -671,16 +670,16 @@ void sn76477_device::log_one_shot_time()
{
if (compute_one_shot_cap_charging_rate() > 0)
{
- LOG(1, "SN76477: One-shot time (23,24): %.4f sec\n", ONE_SHOT_CAP_VOLTAGE_RANGE * (1 / compute_one_shot_cap_charging_rate()));
+ LOG("One-shot time (23,24): %.4f sec\n", ONE_SHOT_CAP_VOLTAGE_RANGE * (1 / compute_one_shot_cap_charging_rate()));
}
else
{
- LOG(1, "SN76477: One-shot time (23,24): N/A\n");
+ LOG("One-shot time (23,24): N/A\n");
}
}
else
{
- LOG(1, "SN76477: One-shot time (23,24): External (cap = %.2fV)\n", m_one_shot_cap_voltage);
+ LOG("One-shot time (23,24): External (cap = %.2fV)\n", m_one_shot_cap_voltage);
}
}
@@ -694,29 +693,29 @@ void sn76477_device::log_slf_freq()
double charging_time = (1 / compute_slf_cap_charging_rate()) * SLF_CAP_VOLTAGE_RANGE;
double discharging_time = (1 / compute_slf_cap_discharging_rate()) * SLF_CAP_VOLTAGE_RANGE;
- LOG(1, "SN76477: SLF frequency (20,21): %.2f Hz\n", 1 / (charging_time + discharging_time));
+ LOG("SLF frequency (20,21): %.2f Hz\n", 1 / (charging_time + discharging_time));
}
else
{
- LOG(1, "SN76477: SLF frequency (20,21): N/A\n");
+ LOG("SLF frequency (20,21): N/A\n");
}
}
else
{
- LOG(1, "SN76477: SLF frequency (20,21): External (cap = %.2fV)\n", m_slf_cap_voltage);
+ LOG("SLF frequency (20,21): External (cap = %.2fV)\n", m_slf_cap_voltage);
}
}
void sn76477_device::log_vco_pitch_voltage()
{
- LOG(1, "SN76477: VCO pitch voltage (19): %.2fV\n", m_pitch_voltage);
+ LOG("VCO pitch voltage (19): %.2fV\n", m_pitch_voltage);
}
void sn76477_device::log_vco_duty_cycle()
{
- LOG(1, "SN76477: VCO duty cycle (16,19): %.0f%%\n", compute_vco_duty_cycle() * 100.0);
+ LOG("VCO duty cycle (16,19): %.0f%%\n", compute_vco_duty_cycle() * 100.0);
}
@@ -729,16 +728,16 @@ void sn76477_device::log_vco_freq()
double min_freq = compute_vco_cap_charging_discharging_rate() / (2 * VCO_CAP_VOLTAGE_RANGE);
double max_freq = compute_vco_cap_charging_discharging_rate() / (2 * VCO_TO_SLF_VOLTAGE_DIFF);
- LOG(1, "SN76477: VCO frequency (17,18): %.2f Hz - %.1f Hz\n", min_freq, max_freq);
+ LOG("VCO frequency (17,18): %.2f Hz - %.1f Hz\n", min_freq, max_freq);
}
else
{
- LOG(1, "SN76477: VCO frequency (17,18): N/A\n");
+ LOG("VCO frequency (17,18): N/A\n");
}
}
else
{
- LOG(1, "SN76477: VCO frequency (17,18): External (cap = %.2fV)\n", m_vco_cap_voltage);
+ LOG("VCO frequency (17,18): External (cap = %.2fV)\n", m_vco_cap_voltage);
}
}
@@ -750,13 +749,13 @@ void sn76477_device::log_vco_ext_voltage()
double min_freq = compute_vco_cap_charging_discharging_rate() / (2 * VCO_CAP_VOLTAGE_RANGE);
double max_freq = compute_vco_cap_charging_discharging_rate() / (2 * VCO_TO_SLF_VOLTAGE_DIFF);
- LOG(1, "SN76477: VCO ext. voltage (16): %.2fV (%.2f Hz)\n",
+ LOG("VCO ext. voltage (16): %.2fV (%.2f Hz)\n",
m_vco_voltage,
min_freq + ((max_freq - min_freq) * m_vco_voltage / VCO_MAX_EXT_VOLTAGE));
}
else
{
- LOG(1, "SN76477: VCO ext. voltage (16): %.2fV (saturated, no output)\n", m_vco_voltage);
+ LOG("VCO ext. voltage (16): %.2fV (saturated, no output)\n", m_vco_voltage);
}
}
@@ -765,17 +764,17 @@ void sn76477_device::log_noise_gen_freq()
{
if (m_noise_clock_ext)
{
- LOG(1, "SN76477: Noise gen frequency (4): External\n");
+ LOG("Noise gen frequency (4): External\n");
}
else
{
if (compute_noise_gen_freq() > 0)
{
- LOG(1, "SN76477: Noise gen frequency (4): %d Hz\n", compute_noise_gen_freq());
+ LOG("Noise gen frequency (4): %d Hz\n", compute_noise_gen_freq());
}
else
{
- LOG(1, "SN76477: Noise gen frequency (4): N/A\n");
+ LOG("Noise gen frequency (4): N/A\n");
}
}
}
@@ -794,21 +793,21 @@ void sn76477_device::log_noise_filter_freq()
double charging_time = (1 / charging_rate) * NOISE_CAP_VOLTAGE_RANGE;
double discharging_time = (1 / charging_rate) * NOISE_CAP_VOLTAGE_RANGE;
- LOG(1, "SN76477: Noise filter frequency (5,6): %.0f Hz\n", 1 / (charging_time + discharging_time));
+ LOG("Noise filter frequency (5,6): %.0f Hz\n", 1 / (charging_time + discharging_time));
}
else
{
- LOG(1, "SN76477: Noise filter frequency (5,6): Very Large (Filtering Disabled)\n");
+ LOG("Noise filter frequency (5,6): Very Large (Filtering Disabled)\n");
}
}
else
{
- LOG(1, "SN76477: Noise filter frequency (5,6): N/A\n");
+ LOG("Noise filter frequency (5,6): N/A\n");
}
}
else
{
- LOG(1, "SN76477: Noise filter frequency (5,6): External (cap = %.2fV)\n", m_noise_filter_cap);
+ LOG("Noise filter frequency (5,6): External (cap = %.2fV)\n", m_noise_filter_cap);
}
}
@@ -819,16 +818,16 @@ void sn76477_device::log_attack_time()
{
if (compute_attack_decay_cap_charging_rate() > 0)
{
- LOG(1, "SN76477: Attack time (8,10): %.4f sec\n", AD_CAP_VOLTAGE_RANGE * (1 / compute_attack_decay_cap_charging_rate()));
+ LOG("Attack time (8,10): %.4f sec\n", AD_CAP_VOLTAGE_RANGE * (1 / compute_attack_decay_cap_charging_rate()));
}
else
{
- LOG(1, "SN76477: Attack time (8,10): N/A\n");
+ LOG("Attack time (8,10): N/A\n");
}
}
else
{
- LOG(1, "SN76477: Attack time (8,10): External (cap = %.2fV)\n", m_attack_decay_cap_voltage);
+ LOG("Attack time (8,10): External (cap = %.2fV)\n", m_attack_decay_cap_voltage);
}
}
@@ -839,23 +838,23 @@ void sn76477_device::log_decay_time()
{
if (compute_attack_decay_cap_discharging_rate() > 0)
{
- LOG(1, "SN76477: Decay time (7,8): %.4f sec\n", AD_CAP_VOLTAGE_RANGE * (1 / compute_attack_decay_cap_discharging_rate()));
+ LOG("Decay time (7,8): %.4f sec\n", AD_CAP_VOLTAGE_RANGE * (1 / compute_attack_decay_cap_discharging_rate()));
}
else
{
- LOG(1, "SN76477: Decay time (8,10): N/A\n");
+ LOG("Decay time (8,10): N/A\n");
}
}
else
{
- LOG(1, "SN76477: Decay time (7, 8): External (cap = %.2fV)\n", m_attack_decay_cap_voltage);
+ LOG("Decay time (7, 8): External (cap = %.2fV)\n", m_attack_decay_cap_voltage);
}
}
void sn76477_device::log_voltage_out()
{
- LOG(1, "SN76477: Voltage OUT range (11,12): %.2fV - %.2fV (clips above %.2fV)\n",
+ LOG("Voltage OUT range (11,12): %.2fV - %.2fV (clips above %.2fV)\n",
OUT_CENTER_LEVEL_VOLTAGE + compute_center_to_peak_voltage_out() * out_neg_gain[(int)(AD_CAP_VOLTAGE_MAX * 10)],
OUT_CENTER_LEVEL_VOLTAGE + compute_center_to_peak_voltage_out() * out_pos_gain[(int)(AD_CAP_VOLTAGE_MAX * 10)],
OUT_HIGH_CLIP_THRESHOLD);
@@ -899,7 +898,7 @@ void sn76477_device::open_wav_file()
m_file = util::wav_open(wav_file_name, m_our_sample_rate, 2);
- LOG(1, "SN76477: Logging output: %s\n", wav_file_name);
+ LOG("Logging output: %s\n", wav_file_name);
}
@@ -951,7 +950,7 @@ inline uint32_t sn76477_device::generate_next_real_noise_bit()
*
*****************************************************************************/
-WRITE_LINE_MEMBER(sn76477_device::enable_w)
+void sn76477_device::enable_w(int state)
{
CHECK_BOOLEAN;
@@ -983,7 +982,7 @@ WRITE_LINE_MEMBER(sn76477_device::enable_w)
*
*****************************************************************************/
-WRITE_LINE_MEMBER( sn76477_device::mixer_a_w )
+void sn76477_device::mixer_a_w(int state)
{
CHECK_BOOLEAN;
@@ -997,7 +996,7 @@ WRITE_LINE_MEMBER( sn76477_device::mixer_a_w )
}
}
-WRITE_LINE_MEMBER( sn76477_device::mixer_b_w )
+void sn76477_device::mixer_b_w(int state)
{
CHECK_BOOLEAN;
@@ -1011,7 +1010,7 @@ WRITE_LINE_MEMBER( sn76477_device::mixer_b_w )
}
}
-WRITE_LINE_MEMBER( sn76477_device::mixer_c_w )
+void sn76477_device::mixer_c_w(int state)
{
CHECK_BOOLEAN;
@@ -1031,7 +1030,7 @@ WRITE_LINE_MEMBER( sn76477_device::mixer_c_w )
*
*****************************************************************************/
-WRITE_LINE_MEMBER( sn76477_device::envelope_1_w )
+void sn76477_device::envelope_1_w(int state)
{
CHECK_BOOLEAN;
@@ -1045,7 +1044,7 @@ WRITE_LINE_MEMBER( sn76477_device::envelope_1_w )
}
}
-WRITE_LINE_MEMBER( sn76477_device::envelope_2_w )
+void sn76477_device::envelope_2_w(int state)
{
CHECK_BOOLEAN;
@@ -1065,7 +1064,7 @@ WRITE_LINE_MEMBER( sn76477_device::envelope_2_w )
*
*****************************************************************************/
-WRITE_LINE_MEMBER( sn76477_device::vco_w )
+void sn76477_device::vco_w(int state)
{
CHECK_BOOLEAN;
@@ -1363,7 +1362,7 @@ void sn76477_device::pitch_voltage_w(double data)
*
*****************************************************************************/
-WRITE_LINE_MEMBER( sn76477_device::noise_clock_w )
+void sn76477_device::noise_clock_w(int state)
{
CHECK_BOOLEAN;
diff --git a/src/devices/sound/sn76477.h b/src/devices/sound/sn76477.h
index 6aa4c10c9f0..f415d113248 100644
--- a/src/devices/sound/sn76477.h
+++ b/src/devices/sound/sn76477.h
@@ -104,14 +104,14 @@ public:
/* these functions take 0 or 1 as a logic input */
- WRITE_LINE_MEMBER( enable_w ); /* active LO, 0 = enabled, 1 = disabled */
- WRITE_LINE_MEMBER( mixer_a_w );
- WRITE_LINE_MEMBER( mixer_b_w );
- WRITE_LINE_MEMBER( mixer_c_w );
- WRITE_LINE_MEMBER( envelope_1_w );
- WRITE_LINE_MEMBER( envelope_2_w );
- WRITE_LINE_MEMBER( vco_w ); /* 0 = external, 1 = controlled by SLF */
- WRITE_LINE_MEMBER( noise_clock_w ); /* noise clock write, if noise_clock_res = 0 */
+ void enable_w(int state); /* active LO, 0 = enabled, 1 = disabled */
+ void mixer_a_w(int state);
+ void mixer_b_w(int state);
+ void mixer_c_w(int state);
+ void envelope_1_w(int state);
+ void envelope_2_w(int state);
+ void vco_w(int state); /* 0 = external, 1 = controlled by SLF */
+ void noise_clock_w(int state); /* noise clock write, if noise_clock_res = 0 */
/* these functions take a resistor value in Ohms */
void one_shot_res_w(double data);
diff --git a/src/devices/sound/sn76496.cpp b/src/devices/sound/sn76496.cpp
index 96c6a3ba789..44460be345e 100644
--- a/src/devices/sound/sn76496.cpp
+++ b/src/devices/sound/sn76496.cpp
@@ -227,24 +227,20 @@ segapsg_device::segapsg_device(const machine_config &mconfig, const char *tag, d
void sn76496_base_device::device_start()
{
int sample_rate = clock()/2;
- int i;
- double out;
int gain;
- m_ready_handler.resolve_safe();
-
m_sound = stream_alloc(0, (m_stereo? 2:1), sample_rate);
- for (i = 0; i < 4; i++) m_volume[i] = 0;
+ for (int i = 0; i < 4; i++) m_volume[i] = 0;
m_last_register = m_sega_style_psg?3:0; // Sega VDP PSG defaults to selected period reg for 2nd channel
- for (i = 0; i < 8; i+=2)
+ for (int i = 0; i < 8; i+=2)
{
m_register[i] = 0;
m_register[i + 1] = 0x0; // volume = 0x0 (max volume) on reset; this needs testing on chips other than SN76489A and Sega VDP PSG
}
- for (i = 0; i < 4; i++)
+ for (int i = 0; i < 4; i++)
{
m_output[i] = 0;
m_period[i] = 0;
@@ -263,12 +259,12 @@ void sn76496_base_device::device_start()
gain &= 0xff;
// increase max output basing on gain (0.2 dB per step)
- out = MAX_OUTPUT / 4; // four channels, each gets 1/4 of the total range
+ double out = MAX_OUTPUT / 4; // four channels, each gets 1/4 of the total range
while (gain-- > 0)
out *= 1.023292992; // = (10 ^ (0.2/20))
// build volume table (2dB per step)
- for (i = 0; i < 15; i++)
+ for (int i = 0; i < 15; i++)
{
// limit volume to avoid clipping
if (out > MAX_OUTPUT / 4) m_vol_table[i] = MAX_OUTPUT / 4;
diff --git a/src/devices/sound/sn76496.h b/src/devices/sound/sn76496.h
index f5484248715..052524d5874 100644
--- a/src/devices/sound/sn76496.h
+++ b/src/devices/sound/sn76496.h
@@ -24,7 +24,7 @@ public:
auto ready_cb() { return m_ready_handler.bind(); }
void stereo_w(u8 data);
void write(u8 data);
- DECLARE_READ_LINE_MEMBER( ready_r ) { return m_ready_state ? 1 : 0; }
+ int ready_r() { return m_ready_state ? 1 : 0; }
protected:
sn76496_base_device(const machine_config &mconfig, device_type type, const char *tag,
diff --git a/src/devices/sound/sp0250.cpp b/src/devices/sound/sp0250.cpp
index 9544adb3c38..2c4384888e9 100644
--- a/src/devices/sound/sp0250.cpp
+++ b/src/devices/sound/sp0250.cpp
@@ -68,8 +68,7 @@ void sp0250_device::device_start()
// if a DRQ callback is offered, run a timer at the frame rate
// to ensure the DRQ gets picked up in a timely manner
- m_drq.resolve_safe();
- if (!m_drq.isnull())
+ if (!m_drq.isunset())
{
m_drq(ASSERT_LINE);
attotime period = attotime::from_hz(frame_rate);
@@ -110,11 +109,6 @@ void sp0250_device::device_reset()
load_values();
}
-TIMER_CALLBACK_MEMBER(sp0250_device::delayed_stream_update)
-{
- m_stream->update();
-}
-
static uint16_t sp0250_ga(uint8_t v)
{
return (v & 0x1f) << (v>>5);
@@ -182,7 +176,7 @@ void sp0250_device::write(uint8_t data)
}
-uint8_t sp0250_device::drq_r()
+int sp0250_device::drq_r()
{
m_stream->update();
return (m_fifo_pos == 15) ? CLEAR_LINE : ASSERT_LINE;
diff --git a/src/devices/sound/sp0250.h b/src/devices/sound/sp0250.h
index 31c32ef4d5d..5dfd3742e52 100644
--- a/src/devices/sound/sp0250.h
+++ b/src/devices/sound/sp0250.h
@@ -14,7 +14,7 @@ public:
void set_pwm_mode() { m_pwm_mode = true; }
void write(uint8_t data);
- uint8_t drq_r();
+ int drq_r();
protected:
// device-level overrides
@@ -23,8 +23,7 @@ protected:
// sound stream update overrides
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
-
- TIMER_CALLBACK_MEMBER(delayed_stream_update);
+ TIMER_CALLBACK_MEMBER(delayed_stream_update) { m_stream->update(); }
private:
// internal helpers
diff --git a/src/devices/sound/sp0256.cpp b/src/devices/sound/sp0256.cpp
index 7f709ec90c0..3486f710be3 100644
--- a/src/devices/sound/sp0256.cpp
+++ b/src/devices/sound/sp0256.cpp
@@ -23,7 +23,6 @@
#include "emu.h"
#include "sp0256.h"
-#define LOG_GENERAL (1U << 0)
#define LOG_FIFO (1U << 1)
//#define VERBOSE (LOG_GENERAL | LOG_FIFO)
@@ -56,10 +55,10 @@ sp0256_device::sp0256_device(const machine_config &mconfig, const char *tag, dev
, device_sound_interface(mconfig, *this)
, m_rom(*this, DEVICE_SELF)
, m_stream(nullptr)
+ , m_stream_timer(nullptr)
, m_drq_cb(*this)
, m_sby_cb(*this)
, m_scratch()
- , m_lrq_timer(nullptr)
{
}
@@ -69,12 +68,19 @@ sp0256_device::sp0256_device(const machine_config &mconfig, const char *tag, dev
void sp0256_device::device_start()
{
- m_drq_cb.resolve_safe();
- m_sby_cb.resolve_safe();
m_drq_cb(1);
m_sby_cb(1);
+ m_sby_line = 1;
- m_stream = stream_alloc(0, 1, clock() / CLOCK_DIVIDER);
+ int sample_rate = clock() / CLOCK_DIVIDER;
+ m_stream = stream_alloc(0, 1, sample_rate);
+
+ // if callbacks are used, update the stream at sample rate frequency to ensure they get picked up in a timely matter
+ if (!m_drq_cb.isunset() || !m_sby_cb.isunset())
+ {
+ m_stream_timer = timer_alloc(FUNC(sp0256_device::delayed_stream_update), this);
+ m_stream_timer->adjust(attotime::from_hz(sample_rate), 0, attotime::from_hz(sample_rate));
+ }
/* -------------------------------------------------------------------- */
/* Configure our internal variables. */
@@ -82,7 +88,7 @@ void sp0256_device::device_start()
m_filt.rng = 1;
/* -------------------------------------------------------------------- */
- /* Allocate a scratch buffer for generating ~10kHz samples. */
+ /* Allocate a scratch buffer for generating ~10kHz samples. */
/* -------------------------------------------------------------------- */
m_scratch = std::make_unique<int16_t[]>(SCBUF_SIZE);
save_pointer(NAME(m_scratch), SCBUF_SIZE);
@@ -94,7 +100,7 @@ void sp0256_device::device_start()
/* -------------------------------------------------------------------- */
m_halted = 1;
m_filt.rpt = -1;
- m_lrq = 0x8000;
+ m_lrq = 1;
m_page = 0x1000 << 3;
m_silent = 1;
@@ -106,8 +112,6 @@ void sp0256_device::device_start()
// TODO: because of this, check if the bitrev functions are even used anywhere else
// bitrevbuff(m_rom, 0, 0xffff);
- m_lrq_timer = timer_alloc(FUNC(sp0256_device::set_lrq_timer_proc), this);
-
// save device variables
save_item(NAME(m_sby_line));
save_item(NAME(m_cur_len));
@@ -126,6 +130,7 @@ void sp0256_device::device_start()
save_item(NAME(m_fifo_tail));
save_item(NAME(m_fifo_bitp));
save_item(NAME(m_fifo));
+
// save filter variables
save_item(NAME(m_filt.rpt));
save_item(NAME(m_filt.cnt));
@@ -146,6 +151,9 @@ void sp0256_device::device_start()
void sp0256_device::device_reset()
{
+ if (machine().time() > attotime::zero)
+ m_stream->update();
+
// reset FIFO and SP0256
m_fifo_head = m_fifo_tail = m_fifo_bitp = 0;
@@ -153,10 +161,10 @@ void sp0256_device::device_reset()
m_halted = 1;
m_filt.rpt = -1;
m_filt.rng = 1;
- m_lrq = 0x8000;
- m_ald = 0x0000;
- m_pc = 0x0000;
- m_stack = 0x0000;
+ m_lrq = 1;
+ m_ald = 0;
+ m_pc = 0;
+ m_stack = 0;
m_fifo_sel = 0;
m_mode = 0;
m_page = 0x1000 << 3;
@@ -164,9 +172,6 @@ void sp0256_device::device_reset()
m_sby_line = 0;
m_drq_cb(1);
SET_SBY(1);
-
- m_lrq = 0;
- m_lrq_timer->adjust(attotime::from_ticks(50, m_clock));
}
@@ -730,7 +735,7 @@ void sp0256_device::bitrevbuff(uint8_t *buffer, unsigned int start, unsigned int
/* ======================================================================== */
/* SP0256_GETB -- Get up to 8 bits at the current PC. */
/* ======================================================================== */
-uint32_t sp0256_device::getb( int len )
+uint32_t sp0256_device::getb(int len)
{
uint32_t data;
uint32_t d0, d1;
@@ -759,7 +764,8 @@ uint32_t sp0256_device::getb( int len )
m_fifo_tail++;
m_fifo_bitp -= 10;
}
- } else
+ }
+ else
{
/* ---------------------------------------------------------------- */
/* Figure out which ROMs are being fetched into, and grab two */
@@ -792,8 +798,8 @@ uint32_t sp0256_device::getb( int len )
/* ======================================================================== */
void sp0256_device::micro()
{
- uint8_t immed4;
- uint8_t opcode;
+ uint8_t immed4;
+ uint8_t opcode;
uint16_t cr;
int ctrl_xfer;
int repeat;
@@ -813,7 +819,7 @@ void sp0256_device::micro()
m_pc = m_ald | (0x1000 << 3);
m_fifo_sel = 0;
m_halted = 0;
- m_lrq = 0x8000;
+ m_lrq = 1;
m_ald = 0;
for (i = 0; i < 16; i++)
m_filt.r[i] = 0;
@@ -826,7 +832,7 @@ void sp0256_device::micro()
if (m_halted)
{
m_filt.rpt = 1;
- m_lrq = 0x8000;
+ m_lrq = 1;
m_ald = 0;
for (i = 0; i < 16; i++)
m_filt.r[i] = 0;
@@ -867,10 +873,12 @@ void sp0256_device::micro()
if (immed4) /* SETPAGE */
{
m_page = bitrev32(immed4) >> 13;
- } else
+ }
+
/* -------------------------------------------------------- */
/* Otherwise, this is an RTS / HLT. */
/* -------------------------------------------------------- */
+ else
{
uint32_t btrg;
@@ -890,7 +898,8 @@ void sp0256_device::micro()
m_halted = 1;
m_pc = 0;
ctrl_xfer = 1;
- } else
+ }
+ else
{
m_pc = btrg;
ctrl_xfer = 1;
@@ -907,7 +916,7 @@ void sp0256_device::micro()
case 0xE:
case 0xD:
{
- int btrg;
+ uint32_t btrg;
/* -------------------------------------------------------- */
/* Figure out our branch target. */
@@ -1143,6 +1152,8 @@ void sp0256_device::micro()
void sp0256_device::ald_w(uint8_t data)
{
+ m_stream->update();
+
/* ---------------------------------------------------------------- */
/* Drop writes to the ALD register if we're busy. */
/* ---------------------------------------------------------------- */
@@ -1158,66 +1169,59 @@ void sp0256_device::ald_w(uint8_t data)
/* get the new PC address. */
/* ---------------------------------------------------------------- */
m_lrq = 0;
- m_ald = (0xff & data) << 4;
+ m_ald = data << 4;
m_drq_cb(0);
SET_SBY(0);
-
- return;
}
-READ_LINE_MEMBER( sp0256_device::lrq_r )
+int sp0256_device::lrq_r()
{
- // force stream update
m_stream->update();
-
- return m_lrq == 0x8000;
+ return m_lrq;
}
-READ_LINE_MEMBER( sp0256_device::sby_r )
+int sp0256_device::sby_r()
{
- // TODO: force stream update??
-
+ m_stream->update();
return m_sby_line;
}
uint16_t sp0256_device::spb640_r(offs_t offset)
{
+ m_stream->update();
+ offset &= 1;
+
/* -------------------------------------------------------------------- */
/* Offset 0 returns the SP0256 LRQ status on bit 15. */
/* -------------------------------------------------------------------- */
if (offset == 0)
{
- return m_lrq;
+ return m_lrq << 15;
}
/* -------------------------------------------------------------------- */
/* Offset 1 returns the SPB640 FIFO full status on bit 15. */
/* -------------------------------------------------------------------- */
- if (offset == 1)
+ else
{
return (m_fifo_head - m_fifo_tail) >= 64 ? 0x8000 : 0;
}
-
- /* -------------------------------------------------------------------- */
- /* Just return 255 for all other addresses in our range. */
- /* -------------------------------------------------------------------- */
- return 0x00ff;
}
void sp0256_device::spb640_w(offs_t offset, uint16_t data)
{
+ m_stream->update();
+ offset &= 1;
+
if (offset == 0)
{
ald_w(data & 0xff);
- return;
}
-
- if (offset == 1)
+ else
{
/* ---------------------------------------------------------------- */
/* If Bit 10 is set, reset the FIFO, and SP0256. */
/* ---------------------------------------------------------------- */
-
if (data & 0x400)
{
m_fifo_head = m_fifo_tail = m_fifo_bitp = 0;
@@ -1237,13 +1241,10 @@ void sp0256_device::spb640_w(offs_t offset, uint16_t data)
/* ---------------------------------------------------------------- */
/* FIFO up the lower 10 bits of the data. */
/* ---------------------------------------------------------------- */
-
LOG("spb640: WR_FIFO %.3X %d.%d %d\n", data & 0x3ff,
m_fifo_tail, m_fifo_bitp, m_fifo_head);
m_fifo[m_fifo_head++ & 63] = data & 0x3ff;
-
- return;
}
}
@@ -1254,11 +1255,6 @@ void sp0256_device::set_clock(int clock)
}
-TIMER_CALLBACK_MEMBER(sp0256_device::set_lrq_timer_proc)
-{
- m_lrq = 0x8000;
-}
-
//-------------------------------------------------
// sound_stream_update - handle a stream update
//-------------------------------------------------
@@ -1267,7 +1263,6 @@ void sp0256_device::sound_stream_update(sound_stream &stream, std::vector<read_s
{
auto &output = outputs[0];
int output_index = 0;
- int length, did_samp/*, old_idx*/;
while (output_index < output.samples())
{
@@ -1275,7 +1270,6 @@ void sp0256_device::sound_stream_update(sound_stream &stream, std::vector<read_s
/* First, drain as much of our scratch buffer as we can into the */
/* sound buffer. */
/* ---------------------------------------------------------------- */
-
while (m_sc_tail != m_sc_head)
{
output.put_int(output_index++, m_scratch[m_sc_tail++ & SCBUF_MASK], 32768);
@@ -1291,14 +1285,13 @@ void sp0256_device::sound_stream_update(sound_stream &stream, std::vector<read_s
if (output_index > output.samples())
break;
- length = output.samples() - output_index;
+ int length = output.samples() - output_index;
/* ---------------------------------------------------------------- */
/* Process the current set of filter coefficients as long as the */
/* repeat count holds up and we have room in our scratch buffer. */
/* ---------------------------------------------------------------- */
- did_samp = 0;
- //old_idx = m_sc_head;
+ int did_samp = 0;
if (length > 0) do
{
int do_samp;
@@ -1325,7 +1318,7 @@ void sp0256_device::sound_stream_update(sound_stream &stream, std::vector<read_s
for (int x = 0; x < do_samp; x++)
m_scratch[y++ & SCBUF_MASK] = 0;
m_sc_head += do_samp;
- did_samp += do_samp;
+ did_samp += do_samp;
}
else
{
diff --git a/src/devices/sound/sp0256.h b/src/devices/sound/sp0256.h
index 0047b5bb0a3..d1c6d7ab687 100644
--- a/src/devices/sound/sp0256.h
+++ b/src/devices/sound/sp0256.h
@@ -46,12 +46,11 @@ public:
auto standby_callback() { return m_sby_cb.bind(); }
void ald_w(uint8_t data);
- DECLARE_READ_LINE_MEMBER(lrq_r);
- DECLARE_READ_LINE_MEMBER(sby_r);
+ int lrq_r();
+ int sby_r();
uint16_t spb640_r(offs_t offset);
void spb640_w(offs_t offset, uint16_t data);
- TIMER_CALLBACK_MEMBER(set_lrq_timer_proc);
void set_clock(int clock);
void bitrevbuff(uint8_t *buffer, unsigned int start, unsigned int length);
@@ -62,6 +61,7 @@ protected:
// sound stream update overrides
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
+ TIMER_CALLBACK_MEMBER(delayed_stream_update) { m_stream->update(); }
private:
struct lpc12_t
@@ -69,14 +69,14 @@ private:
int update(int num_samp, int16_t *out, uint32_t *optr);
void regdec();
- int rpt, cnt; // Repeat counter, Period down-counter.
+ int rpt, cnt; // Repeat counter, Period down-counter.
uint32_t per, rng; // Period, Amplitude, Random Number Generator
- int amp;
+ int amp;
int16_t f_coef[6]; // F0 through F5.
int16_t b_coef[6]; // B0 through B5.
int16_t z_data[6][2]; // Time-delay data for the filter stages.
uint8_t r[16]; // The encoded register set.
- int interp;
+ int interp;
private:
static int16_t limit(int16_t s);
@@ -95,35 +95,34 @@ private:
}
required_region_ptr<uint8_t> m_rom; // 64K ROM.
- sound_stream *m_stream; // MAME core sound stream
- devcb_write_line m_drq_cb; // Data request callback
- devcb_write_line m_sby_cb; // Standby callback
+ sound_stream *m_stream; // MAME core sound stream
+ emu_timer *m_stream_timer; // For forcing stream update when callbacks are used
+ devcb_write_line m_drq_cb; // Data request callback
+ devcb_write_line m_sby_cb; // Standby callback
int m_sby_line; // Standby line state
int m_cur_len; // Fullness of current sound buffer.
int m_silent; // Flag: SP0256 is silent.
- std::unique_ptr<int16_t[]> m_scratch; // Scratch buffer for audio.
- uint32_t m_sc_head; // Head pointer into scratch circular buf
- uint32_t m_sc_tail; // Tail pointer into scratch circular buf
+ std::unique_ptr<int16_t[]> m_scratch; // Scratch buffer for audio.
+ uint32_t m_sc_head; // Head pointer into scratch circular buf
+ uint32_t m_sc_tail; // Tail pointer into scratch circular buf
- lpc12_t m_filt; // 12-pole filter
+ lpc12_t m_filt; // 12-pole filter
int m_lrq; // Load ReQuest. == 0 if we can accept a load
int m_ald; // Address LoaD. < 0 if no command pending.
int m_pc; // Microcontroller's PC value.
int m_stack; // Microcontroller's PC stack.
int m_fifo_sel; // True when executing from FIFO.
int m_halted; // True when CPU is halted.
- uint32_t m_mode; // Mode register.
- uint32_t m_page; // Page set by SETPAGE
+ uint32_t m_mode; // Mode register.
+ uint32_t m_page; // Page set by SETPAGE
- uint32_t m_fifo_head; // FIFO head pointer (where new data goes).
- uint32_t m_fifo_tail; // FIFO tail pointer (where data comes from).
- uint32_t m_fifo_bitp; // FIFO bit-pointer (for partial decles).
- uint16_t m_fifo[64]; // The 64-decle FIFO.
-
- emu_timer *m_lrq_timer;
+ uint32_t m_fifo_head; // FIFO head pointer (where new data goes).
+ uint32_t m_fifo_tail; // FIFO tail pointer (where data comes from).
+ uint32_t m_fifo_bitp; // FIFO bit-pointer (for partial decles).
+ uint16_t m_fifo[64]; // The 64-decle FIFO.
};
DECLARE_DEVICE_TYPE(SP0256, sp0256_device)
diff --git a/src/devices/sound/spkrdev.cpp b/src/devices/sound/spkrdev.cpp
index 7d9333d02cb..19d1f1813d6 100644
--- a/src/devices/sound/spkrdev.cpp
+++ b/src/devices/sound/spkrdev.cpp
@@ -72,7 +72,7 @@
*/
#include "emu.h"
-#include "sound/spkrdev.h"
+#include "spkrdev.h"
// The default is 1-bit, but can be customized with set_levels.
static constexpr double default_levels[2] = { 0.0, 1.0 };
diff --git a/src/devices/sound/spu.cpp b/src/devices/sound/spu.cpp
index 7cc987a8067..9e3a95b0357 100644
--- a/src/devices/sound/spu.cpp
+++ b/src/devices/sound/spu.cpp
@@ -950,6 +950,7 @@ spu_device::spu_device(const machine_config &mconfig, const char *tag, device_t
spu_device::spu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
device_t(mconfig, SPU, tag, owner, clock),
device_sound_interface(mconfig, *this),
+ m_stream_flags(STREAM_DEFAULT_FLAGS),
m_irq_handler(*this),
dirty_flags(-1),
status_enabled(false),
@@ -959,14 +960,15 @@ spu_device::spu_device(const machine_config &mconfig, const char *tag, device_t
{
}
-//-------------------------------------------------
-// static_set_irqf - configuration helper to set
-// the IRQ callback
-//-------------------------------------------------
-
void spu_device::device_start()
{
- m_irq_handler.resolve_safe();
+ spu_base_frequency_hz = clock() / 768.0f;
+ generate_linear_rate_table();
+ generate_pos_exp_rate_table();
+ generate_neg_exp_rate_table();
+ generate_decay_rate_table();
+ generate_linear_release_rate_table();
+ generate_exp_release_rate_table();
voice=new voiceinfo [24];
spu_ram=std::make_unique<unsigned char []>(spu_ram_size);
@@ -1095,12 +1097,13 @@ void spu_device::init_stream()
{
const unsigned int hz=44100;
- m_stream = stream_alloc(0, 2, hz);
+ // TODO: Rewrite SPU stream update code to work such that Taiko no Tatsujin no longer needs synchronous streams
+ m_stream = stream_alloc(0, 2, hz, m_stream_flags);
rev=new reverb(hz);
cdda_freq=(unsigned int)((44100.0f/(float)hz)*4096.0f);
- freq_multiplier=(float)spu_base_frequency_hz/(float)hz;
+ freq_multiplier=spu_base_frequency_hz/(float)hz;
}
//
@@ -2531,6 +2534,13 @@ void spu_device::update_reverb()
{
if (dirty_flags&dirtyflag_reverb)
{
+ // TODO: Handle cases where reverb present can't be found better
+ // If a save state is loaded and has reverb values that don't match a preset
+ // then spu_reverb_cfg is never set so the reverb settings won't be the same as
+ // when the save state was created.
+ // This only becomes an issue when loading save states from the command line
+ // because if you load a save state from within MAME it will hold the last used
+ // spu_reverb_cfg and reuse that value.
cur_reverb_preset=find_reverb_preset((unsigned short *)&reg[0x1c0]);
if (cur_reverb_preset==nullptr)
diff --git a/src/devices/sound/spu.h b/src/devices/sound/spu.h
index 7aee602e38c..0768006c0fb 100644
--- a/src/devices/sound/spu.h
+++ b/src/devices/sound/spu.h
@@ -25,8 +25,9 @@ class spu_device : public device_t, public device_sound_interface
dirtyflag_irq=0x04000000
};
+ sound_stream_flags m_stream_flags;
+
protected:
- static constexpr unsigned int spu_base_frequency_hz=44100;
class reverb;
// device-level overrides
@@ -37,14 +38,13 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
- static constexpr float ms_to_rate(float ms) { return 1.0f / (ms * (float(spu_base_frequency_hz) / 1000.0f)); }
- static constexpr float s_to_rate(float s) { return ms_to_rate(s * 1000.0f); }
- static const float linear_rate[];
- static const float pos_exp_rate[];
- static const float neg_exp_rate[];
- static const float decay_rate[];
- static const float linear_release_rate[];
- static const float exp_release_rate[];
+ float spu_base_frequency_hz;
+ float linear_rate[108];
+ float pos_exp_rate[100];
+ float neg_exp_rate[108];
+ float decay_rate[16];
+ float linear_release_rate[27];
+ float exp_release_rate[27];
// internal state
devcb_write_line m_irq_handler;
@@ -145,6 +145,9 @@ protected:
static reverb_preset reverb_presets[];
static reverb_params *spu_reverb_cfg;
+ float ms_to_rate(float ms) const { return 1.0f / (ms * (spu_base_frequency_hz / 1000.0f)); }
+ float s_to_rate(float s) const { return ms_to_rate(s * 1000.0f); }
+
void key_on(const int v);
void key_off(const int v);
bool update_envelope(const int v);
@@ -187,17 +190,25 @@ protected:
void write_cache_pointer(outfile *fout, cache_pointer *cp, sample_loop_cache *lc=nullptr);
void read_cache_pointer(infile *fin, cache_pointer *cp, sample_loop_cache **lc=nullptr);
#endif
- static float get_linear_rate(const int n);
- static float get_linear_rate_neg_phase(const int n);
- static float get_pos_exp_rate(const int n);
- static float get_pos_exp_rate_neg_phase(const int n);
- static float get_neg_exp_rate(const int n);
- static float get_neg_exp_rate_neg_phase(const int n);
- static float get_decay_rate(const int n);
- static float get_sustain_level(const int n);
- static float get_linear_release_rate(const int n);
- static float get_exp_release_rate(const int n);
- static reverb_preset *find_reverb_preset(const unsigned short *param);
+
+ void generate_linear_rate_table();
+ void generate_pos_exp_rate_table();
+ void generate_neg_exp_rate_table();
+ void generate_decay_rate_table();
+ void generate_linear_release_rate_table();
+ void generate_exp_release_rate_table();
+
+ float get_linear_rate(const int n);
+ float get_linear_rate_neg_phase(const int n);
+ float get_pos_exp_rate(const int n);
+ float get_pos_exp_rate_neg_phase(const int n);
+ float get_neg_exp_rate(const int n);
+ float get_neg_exp_rate_neg_phase(const int n);
+ float get_decay_rate(const int n);
+ float get_sustain_level(const int n);
+ float get_linear_release_rate(const int n);
+ float get_exp_release_rate(const int n);
+ reverb_preset *find_reverb_preset(const unsigned short *param);
public:
spu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, psxcpu_device *cpu);
@@ -205,6 +216,7 @@ public:
// configuration helpers
auto irq_handler() { return m_irq_handler.bind(); }
+ void set_stream_flags(sound_stream_flags flags) { m_stream_flags = flags; }
void dma_read( uint32_t *ram, uint32_t n_address, int32_t n_size );
void dma_write( uint32_t *ram, uint32_t n_address, int32_t n_size );
diff --git a/src/devices/sound/spu_tables.cpp b/src/devices/sound/spu_tables.cpp
index 1bf9c851b00..39cdf87d4b7 100644
--- a/src/devices/sound/spu_tables.cpp
+++ b/src/devices/sound/spu_tables.cpp
@@ -4,420 +4,6 @@
#include "spu.h"
#include "spureverb.h"
-const float spu_device::linear_rate[]=
-{
- ms_to_rate(0.05f),
- ms_to_rate(0.06f),
- ms_to_rate(0.07f),
- ms_to_rate(0.09f),
- ms_to_rate(0.10f),
- ms_to_rate(0.12f),
- ms_to_rate(0.15f),
- ms_to_rate(0.18f),
- ms_to_rate(0.21f),
- ms_to_rate(0.24f),
- ms_to_rate(0.29f),
- ms_to_rate(0.36f),
- ms_to_rate(0.41f),
- ms_to_rate(0.48f),
- ms_to_rate(0.58f),
- ms_to_rate(0.73f),
- ms_to_rate(0.83f),
- ms_to_rate(0.97f),
- ms_to_rate(1.2f),
- ms_to_rate(1.5f),
- ms_to_rate(1.7f),
- ms_to_rate(1.9f),
- ms_to_rate(2.3f),
- ms_to_rate(2.9f),
- ms_to_rate(3.3f),
- ms_to_rate(3.9f),
- ms_to_rate(4.6f),
- ms_to_rate(5.8f),
- ms_to_rate(6.6f),
- ms_to_rate(7.7f),
- ms_to_rate(9.3f),
- ms_to_rate(12.0f),
- ms_to_rate(13.0f),
- ms_to_rate(15.0f),
- ms_to_rate(19.0f),
- ms_to_rate(23.0f),
- ms_to_rate(27.0f),
- ms_to_rate(31.0f),
- ms_to_rate(37.0f),
- ms_to_rate(46.0f),
- ms_to_rate(53.0f),
- ms_to_rate(62.0f),
- ms_to_rate(74.0f),
- ms_to_rate(93.0f),
- s_to_rate(0.11f),
- s_to_rate(0.12f),
- s_to_rate(0.15f),
- s_to_rate(0.19f),
- s_to_rate(0.21f),
- s_to_rate(0.25f),
- s_to_rate(0.30f),
- s_to_rate(0.37f),
- s_to_rate(0.42f),
- s_to_rate(0.50f),
- s_to_rate(0.59f),
- s_to_rate(0.74f),
- s_to_rate(0.85f),
- s_to_rate(0.99f),
- s_to_rate(1.2f),
- s_to_rate(1.5f),
- s_to_rate(1.7f),
- s_to_rate(2.0f),
- s_to_rate(2.4f),
- s_to_rate(3.0f),
- s_to_rate(3.4f),
- s_to_rate(4.0f),
- s_to_rate(4.8f),
- s_to_rate(5.9f),
- s_to_rate(6.8f),
- s_to_rate(7.9f),
- s_to_rate(9.5f),
- s_to_rate(12.0f),
- s_to_rate(14.0f),
- s_to_rate(16.0f),
- s_to_rate(19.0f),
- s_to_rate(24.0f),
- s_to_rate(27.0f),
- s_to_rate(32.0f),
- s_to_rate(38.0f),
- s_to_rate(48.0f),
- s_to_rate(54.0f),
- s_to_rate(63.0f),
- s_to_rate(76.0f),
- s_to_rate(95.0f),
- s_to_rate(109.0f),
- s_to_rate(127.0f),
- s_to_rate(152.0f),
- s_to_rate(190.0f),
- s_to_rate(218.0f),
- s_to_rate(254.0f),
- s_to_rate(304.0f),
- s_to_rate(380.0f),
- s_to_rate(436.0f),
- s_to_rate(508.0f),
- s_to_rate(608.0f),
- s_to_rate(760.0f),
- s_to_rate(872.0f),
- s_to_rate(1016.0f),
- s_to_rate(1216.0f),
- s_to_rate(1520.0f),
- s_to_rate(1744.0f),
- s_to_rate(2032.0f),
- s_to_rate(2432.0f),
- s_to_rate(3040.0f),
- s_to_rate(3488.0f),
- s_to_rate(4064.0f),
- s_to_rate(4864.0f),
- s_to_rate(6080.0f)
-};
-
-
-const float spu_device::pos_exp_rate[]=
-{
- ms_to_rate(0.09f),
- ms_to_rate(0.11f),
- ms_to_rate(0.13f),
- ms_to_rate(0.16f),
- ms_to_rate(0.18f),
- ms_to_rate(0.21f),
- ms_to_rate(0.25f),
- ms_to_rate(0.32f),
- ms_to_rate(0.36f),
- ms_to_rate(0.42f),
- ms_to_rate(0.51f),
- ms_to_rate(0.64f),
- ms_to_rate(0.73f),
- ms_to_rate(0.85f),
- ms_to_rate(1.0f),
- ms_to_rate(1.3f),
- ms_to_rate(1.5f),
- ms_to_rate(1.7f),
- ms_to_rate(2.0f),
- ms_to_rate(2.5f),
- ms_to_rate(2.9f),
- ms_to_rate(3.4f),
- ms_to_rate(4.1f),
- ms_to_rate(5.1f),
- ms_to_rate(5.8f),
- ms_to_rate(6.8f),
- ms_to_rate(8.1f),
- ms_to_rate(10.0f),
- ms_to_rate(12.0f),
- ms_to_rate(14.0f),
- ms_to_rate(16.0f),
- ms_to_rate(20.0f),
- ms_to_rate(23.0f),
- ms_to_rate(27.0f),
- ms_to_rate(33.0f),
- ms_to_rate(41.0f),
- ms_to_rate(46.0f),
- ms_to_rate(54.0f),
- ms_to_rate(65.0f),
- ms_to_rate(81.0f),
- ms_to_rate(93.0f),
- s_to_rate(0.11f),
- s_to_rate(0.13f),
- s_to_rate(0.16f),
- s_to_rate(0.19f),
- s_to_rate(0.22f),
- s_to_rate(0.26f),
- s_to_rate(0.33f),
- s_to_rate(0.37f),
- s_to_rate(0.43f),
- s_to_rate(0.52f),
- s_to_rate(0.65f),
- s_to_rate(0.74f),
- s_to_rate(0.87f),
- s_to_rate(1.0f),
- s_to_rate(1.3f),
- s_to_rate(1.5f),
- s_to_rate(1.7f),
- s_to_rate(2.1f),
- s_to_rate(2.6f),
- s_to_rate(3.0f),
- s_to_rate(3.5f),
- s_to_rate(4.2f),
- s_to_rate(5.2f),
- s_to_rate(5.9f),
- s_to_rate(6.9f),
- s_to_rate(8.3f),
- s_to_rate(10.0f),
- s_to_rate(12.0f),
- s_to_rate(14.0f),
- s_to_rate(17.0f),
- s_to_rate(21.0f),
- s_to_rate(24.0f),
- s_to_rate(28.0f),
- s_to_rate(33.0f),
- s_to_rate(42.0f),
- s_to_rate(48.0f),
- s_to_rate(55.0f),
- s_to_rate(67.0f),
- s_to_rate(83.0f),
- s_to_rate(95.0f),
- s_to_rate(111.0f),
- s_to_rate(133.0f),
- s_to_rate(166.0f),
- s_to_rate(190.0f),
- s_to_rate(222.0f),
- s_to_rate(266.0f),
- s_to_rate(333.0f),
- s_to_rate(380.0f),
- s_to_rate(444.0f),
- s_to_rate(532.0f),
- s_to_rate(666.0f),
- s_to_rate(760.0f),
- s_to_rate(888.0f),
- s_to_rate(1064.0f),
- s_to_rate(1332.0f),
- s_to_rate(1520.0f),
- s_to_rate(1776.0f),
- s_to_rate(2128.0f),
- s_to_rate(2664.0f)
-};
-
-
-const float spu_device::neg_exp_rate[]=
-{
- ms_to_rate(0.07f),
- ms_to_rate(0.09f),
- ms_to_rate(0.11f),
- ms_to_rate(0.14f),
- ms_to_rate(0.18f),
- ms_to_rate(0.21f),
- ms_to_rate(0.25f),
- ms_to_rate(0.31f),
- ms_to_rate(0.39f),
- ms_to_rate(0.45f),
- ms_to_rate(0.53f),
- ms_to_rate(0.64f),
- ms_to_rate(0.81f),
- ms_to_rate(0.93f),
- ms_to_rate(1.1f),
- ms_to_rate(1.3f),
- ms_to_rate(1.6f),
- ms_to_rate(1.9f),
- ms_to_rate(2.2f),
- ms_to_rate(2.6f),
- ms_to_rate(3.3f),
- ms_to_rate(3.8f),
- ms_to_rate(4.4f),
- ms_to_rate(5.3f),
- ms_to_rate(6.7f),
- ms_to_rate(7.6f),
- ms_to_rate(8.9f),
- ms_to_rate(11.0f),
- ms_to_rate(13.0f),
- ms_to_rate(15.0f),
- ms_to_rate(18.0f),
- ms_to_rate(21.0f),
- ms_to_rate(27.0f),
- ms_to_rate(31.0f),
- ms_to_rate(36.0f),
- ms_to_rate(43.0f),
- ms_to_rate(53.0f),
- ms_to_rate(61.0f),
- ms_to_rate(71.0f),
- ms_to_rate(86.0f),
- s_to_rate(0.11f),
- s_to_rate(0.12f),
- s_to_rate(0.14f),
- s_to_rate(0.17f),
- s_to_rate(0.21f),
- s_to_rate(0.24f),
- s_to_rate(0.29f),
- s_to_rate(0.34f),
- s_to_rate(0.43f),
- s_to_rate(0.49f),
- s_to_rate(0.57f),
- s_to_rate(0.68f),
- s_to_rate(0.86f),
- s_to_rate(0.98f),
- s_to_rate(1.1f),
- s_to_rate(1.4f),
- s_to_rate(1.7f),
- s_to_rate(2.0f),
- s_to_rate(2.3f),
- s_to_rate(2.7f),
- s_to_rate(3.4f),
- s_to_rate(3.9f),
- s_to_rate(4.6f),
- s_to_rate(5.5f),
- s_to_rate(6.8f),
- s_to_rate(7.8f),
- s_to_rate(9.1f),
- s_to_rate(11.0f),
- s_to_rate(14.0f),
- s_to_rate(16.0f),
- s_to_rate(18.0f),
- s_to_rate(22.0f),
- s_to_rate(27.0f),
- s_to_rate(31.0f),
- s_to_rate(36.0f),
- s_to_rate(44.0f),
- s_to_rate(55.0f),
- s_to_rate(63.0f),
- s_to_rate(73.0f),
- s_to_rate(88.0f),
- s_to_rate(109.0f),
- s_to_rate(125.0f),
- s_to_rate(146.0f),
- s_to_rate(175.0f),
- s_to_rate(219.0f),
- s_to_rate(250.0f),
- s_to_rate(292.0f),
- s_to_rate(350.0f),
- s_to_rate(438.0f),
- s_to_rate(500.0f),
- s_to_rate(584.0f),
- s_to_rate(700.0f),
- s_to_rate(876.0f),
- s_to_rate(1000.0f),
- s_to_rate(1168.0f),
- s_to_rate(1400.0f),
- s_to_rate(1752.0f),
- s_to_rate(2000.0f),
- s_to_rate(2336.0f),
- s_to_rate(2800.0f),
- s_to_rate(3504.0f),
- s_to_rate(4000.0f),
- s_to_rate(4672.0f),
- s_to_rate(5600.0f),
- s_to_rate(7008.0f),
- s_to_rate(8000.0f),
- s_to_rate(9344.0f),
- s_to_rate(11200.0f)
-};
-
-
-const float spu_device::decay_rate[16]=
-{
- ms_to_rate(0.07f),
- ms_to_rate(0.18f),
- ms_to_rate(0.39f),
- ms_to_rate(0.81f),
- ms_to_rate(1.6f),
- ms_to_rate(3.3f),
- ms_to_rate(6.7f),
- ms_to_rate(13.0f),
- ms_to_rate(27.0f),
- ms_to_rate(53.0f),
- s_to_rate(0.11f),
- s_to_rate(0.21f),
- s_to_rate(0.43f),
- s_to_rate(0.86f),
- s_to_rate(1.7f),
- s_to_rate(3.4f),
-};
-
-const float spu_device::linear_release_rate[]=
-{
- ms_to_rate(0.04f),
- ms_to_rate(0.09f),
- ms_to_rate(0.18f),
- ms_to_rate(0.36f),
- ms_to_rate(0.73f),
- ms_to_rate(1.5f),
- ms_to_rate(2.9f),
- ms_to_rate(5.8f),
- ms_to_rate(12.0f),
- ms_to_rate(23.0f),
- ms_to_rate(46.0f),
- ms_to_rate(93.0f),
- s_to_rate(0.19f),
- s_to_rate(0.37f),
- s_to_rate(1.74f),
- s_to_rate(1.5f),
- s_to_rate(3.0f),
- s_to_rate(5.9f),
- s_to_rate(12.0f),
- s_to_rate(24.0f),
- s_to_rate(48.0f),
- s_to_rate(95.0f),
- s_to_rate(190.0f),
- s_to_rate(380.0f),
- s_to_rate(760.0f),
- s_to_rate(1520.0f),
- s_to_rate(3040.0f)
-};
-
-
-const float spu_device::exp_release_rate[]=
-{
- ms_to_rate(0.07f),
- ms_to_rate(0.18f),
- ms_to_rate(0.39f),
- ms_to_rate(0.81f),
- ms_to_rate(1.6f),
- ms_to_rate(3.3f),
- ms_to_rate(6.7f),
- ms_to_rate(13.0f),
- ms_to_rate(27.0f),
- ms_to_rate(53.0f),
- s_to_rate(0.11f),
- s_to_rate(0.21f),
- s_to_rate(0.43f),
- s_to_rate(0.86f),
- s_to_rate(1.7f),
- s_to_rate(3.4f),
- s_to_rate(6.8f),
- s_to_rate(14.0f),
- s_to_rate(27.0f),
- s_to_rate(55.0f),
- s_to_rate(109.0f),
- s_to_rate(219.0f),
- s_to_rate(438.0f),
- s_to_rate(876.0f),
- s_to_rate(1752.0f),
- s_to_rate(3504.0f),
- s_to_rate(7008.0f)
-};
-
//
//
@@ -615,9 +201,423 @@ spu_device::reverb_preset spu_device::reverb_presets[]=
//
//
+
+
+void spu_device::generate_linear_rate_table()
+{
+ linear_rate[0] = ms_to_rate(0.05f);
+ linear_rate[1] = ms_to_rate(0.06f);
+ linear_rate[2] = ms_to_rate(0.07f);
+ linear_rate[3] = ms_to_rate(0.09f);
+ linear_rate[4] = ms_to_rate(0.10f);
+ linear_rate[5] = ms_to_rate(0.12f);
+ linear_rate[6] = ms_to_rate(0.15f);
+ linear_rate[7] = ms_to_rate(0.18f);
+ linear_rate[8] = ms_to_rate(0.21f);
+ linear_rate[9] = ms_to_rate(0.24f);
+ linear_rate[10] = ms_to_rate(0.29f);
+ linear_rate[11] = ms_to_rate(0.36f);
+ linear_rate[12] = ms_to_rate(0.41f);
+ linear_rate[13] = ms_to_rate(0.48f);
+ linear_rate[14] = ms_to_rate(0.58f);
+ linear_rate[15] = ms_to_rate(0.73f);
+ linear_rate[16] = ms_to_rate(0.83f);
+ linear_rate[17] = ms_to_rate(0.97f);
+ linear_rate[18] = ms_to_rate(1.2f);
+ linear_rate[19] = ms_to_rate(1.5f);
+ linear_rate[20] = ms_to_rate(1.7f);
+ linear_rate[21] = ms_to_rate(1.9f);
+ linear_rate[22] = ms_to_rate(2.3f);
+ linear_rate[23] = ms_to_rate(2.9f);
+ linear_rate[24] = ms_to_rate(3.3f);
+ linear_rate[25] = ms_to_rate(3.9f);
+ linear_rate[26] = ms_to_rate(4.6f);
+ linear_rate[27] = ms_to_rate(5.8f);
+ linear_rate[28] = ms_to_rate(6.6f);
+ linear_rate[29] = ms_to_rate(7.7f);
+ linear_rate[30] = ms_to_rate(9.3f);
+ linear_rate[31] = ms_to_rate(12.0f);
+ linear_rate[32] = ms_to_rate(13.0f);
+ linear_rate[33] = ms_to_rate(15.0f);
+ linear_rate[34] = ms_to_rate(19.0f);
+ linear_rate[35] = ms_to_rate(23.0f);
+ linear_rate[36] = ms_to_rate(27.0f);
+ linear_rate[37] = ms_to_rate(31.0f);
+ linear_rate[38] = ms_to_rate(37.0f);
+ linear_rate[39] = ms_to_rate(46.0f);
+ linear_rate[40] = ms_to_rate(53.0f);
+ linear_rate[41] = ms_to_rate(62.0f);
+ linear_rate[42] = ms_to_rate(74.0f);
+ linear_rate[43] = ms_to_rate(93.0f);
+ linear_rate[44] = s_to_rate(0.11f);
+ linear_rate[45] = s_to_rate(0.12f);
+ linear_rate[46] = s_to_rate(0.15f);
+ linear_rate[47] = s_to_rate(0.19f);
+ linear_rate[48] = s_to_rate(0.21f);
+ linear_rate[49] = s_to_rate(0.25f);
+ linear_rate[50] = s_to_rate(0.30f);
+ linear_rate[51] = s_to_rate(0.37f);
+ linear_rate[52] = s_to_rate(0.42f);
+ linear_rate[53] = s_to_rate(0.50f);
+ linear_rate[54] = s_to_rate(0.59f);
+ linear_rate[55] = s_to_rate(0.74f);
+ linear_rate[56] = s_to_rate(0.85f);
+ linear_rate[57] = s_to_rate(0.99f);
+ linear_rate[58] = s_to_rate(1.2f);
+ linear_rate[59] = s_to_rate(1.5f);
+ linear_rate[60] = s_to_rate(1.7f);
+ linear_rate[61] = s_to_rate(2.0f);
+ linear_rate[62] = s_to_rate(2.4f);
+ linear_rate[63] = s_to_rate(3.0f);
+ linear_rate[64] = s_to_rate(3.4f);
+ linear_rate[65] = s_to_rate(4.0f);
+ linear_rate[66] = s_to_rate(4.8f);
+ linear_rate[67] = s_to_rate(5.9f);
+ linear_rate[68] = s_to_rate(6.8f);
+ linear_rate[69] = s_to_rate(7.9f);
+ linear_rate[70] = s_to_rate(9.5f);
+ linear_rate[71] = s_to_rate(12.0f);
+ linear_rate[72] = s_to_rate(14.0f);
+ linear_rate[73] = s_to_rate(16.0f);
+ linear_rate[74] = s_to_rate(19.0f);
+ linear_rate[75] = s_to_rate(24.0f);
+ linear_rate[76] = s_to_rate(27.0f);
+ linear_rate[77] = s_to_rate(32.0f);
+ linear_rate[78] = s_to_rate(38.0f);
+ linear_rate[79] = s_to_rate(48.0f);
+ linear_rate[80] = s_to_rate(54.0f);
+ linear_rate[81] = s_to_rate(63.0f);
+ linear_rate[82] = s_to_rate(76.0f);
+ linear_rate[83] = s_to_rate(95.0f);
+ linear_rate[84] = s_to_rate(109.0f);
+ linear_rate[85] = s_to_rate(127.0f);
+ linear_rate[86] = s_to_rate(152.0f);
+ linear_rate[87] = s_to_rate(190.0f);
+ linear_rate[88] = s_to_rate(218.0f);
+ linear_rate[89] = s_to_rate(254.0f);
+ linear_rate[90] = s_to_rate(304.0f);
+ linear_rate[91] = s_to_rate(380.0f);
+ linear_rate[92] = s_to_rate(436.0f);
+ linear_rate[93] = s_to_rate(508.0f);
+ linear_rate[94] = s_to_rate(608.0f);
+ linear_rate[95] = s_to_rate(760.0f);
+ linear_rate[96] = s_to_rate(872.0f);
+ linear_rate[97] = s_to_rate(1016.0f);
+ linear_rate[98] = s_to_rate(1216.0f);
+ linear_rate[99] = s_to_rate(1520.0f);
+ linear_rate[100] = s_to_rate(1744.0f);
+ linear_rate[101] = s_to_rate(2032.0f);
+ linear_rate[102] = s_to_rate(2432.0f);
+ linear_rate[103] = s_to_rate(3040.0f);
+ linear_rate[104] = s_to_rate(3488.0f);
+ linear_rate[105] = s_to_rate(4064.0f);
+ linear_rate[106] = s_to_rate(4864.0f);
+ linear_rate[107] = s_to_rate(6080.0f);
+}
+
+
+void spu_device::generate_pos_exp_rate_table()
+{
+ pos_exp_rate[0] = ms_to_rate(0.09f);
+ pos_exp_rate[1] = ms_to_rate(0.11f);
+ pos_exp_rate[2] = ms_to_rate(0.13f);
+ pos_exp_rate[3] = ms_to_rate(0.16f);
+ pos_exp_rate[4] = ms_to_rate(0.18f);
+ pos_exp_rate[5] = ms_to_rate(0.21f);
+ pos_exp_rate[6] = ms_to_rate(0.25f);
+ pos_exp_rate[7] = ms_to_rate(0.32f);
+ pos_exp_rate[8] = ms_to_rate(0.36f);
+ pos_exp_rate[9] = ms_to_rate(0.42f);
+ pos_exp_rate[10] = ms_to_rate(0.51f);
+ pos_exp_rate[11] = ms_to_rate(0.64f);
+ pos_exp_rate[12] = ms_to_rate(0.73f);
+ pos_exp_rate[13] = ms_to_rate(0.85f);
+ pos_exp_rate[14] = ms_to_rate(1.0f);
+ pos_exp_rate[15] = ms_to_rate(1.3f);
+ pos_exp_rate[16] = ms_to_rate(1.5f);
+ pos_exp_rate[17] = ms_to_rate(1.7f);
+ pos_exp_rate[18] = ms_to_rate(2.0f);
+ pos_exp_rate[19] = ms_to_rate(2.5f);
+ pos_exp_rate[20] = ms_to_rate(2.9f);
+ pos_exp_rate[21] = ms_to_rate(3.4f);
+ pos_exp_rate[22] = ms_to_rate(4.1f);
+ pos_exp_rate[23] = ms_to_rate(5.1f);
+ pos_exp_rate[24] = ms_to_rate(5.8f);
+ pos_exp_rate[25] = ms_to_rate(6.8f);
+ pos_exp_rate[26] = ms_to_rate(8.1f);
+ pos_exp_rate[27] = ms_to_rate(10.0f);
+ pos_exp_rate[28] = ms_to_rate(12.0f);
+ pos_exp_rate[29] = ms_to_rate(14.0f);
+ pos_exp_rate[30] = ms_to_rate(16.0f);
+ pos_exp_rate[31] = ms_to_rate(20.0f);
+ pos_exp_rate[32] = ms_to_rate(23.0f);
+ pos_exp_rate[33] = ms_to_rate(27.0f);
+ pos_exp_rate[34] = ms_to_rate(33.0f);
+ pos_exp_rate[35] = ms_to_rate(41.0f);
+ pos_exp_rate[36] = ms_to_rate(46.0f);
+ pos_exp_rate[37] = ms_to_rate(54.0f);
+ pos_exp_rate[38] = ms_to_rate(65.0f);
+ pos_exp_rate[39] = ms_to_rate(81.0f);
+ pos_exp_rate[40] = ms_to_rate(93.0f);
+ pos_exp_rate[41] = s_to_rate(0.11f);
+ pos_exp_rate[42] = s_to_rate(0.13f);
+ pos_exp_rate[43] = s_to_rate(0.16f);
+ pos_exp_rate[44] = s_to_rate(0.19f);
+ pos_exp_rate[45] = s_to_rate(0.22f);
+ pos_exp_rate[46] = s_to_rate(0.26f);
+ pos_exp_rate[47] = s_to_rate(0.33f);
+ pos_exp_rate[48] = s_to_rate(0.37f);
+ pos_exp_rate[49] = s_to_rate(0.43f);
+ pos_exp_rate[50] = s_to_rate(0.52f);
+ pos_exp_rate[51] = s_to_rate(0.65f);
+ pos_exp_rate[52] = s_to_rate(0.74f);
+ pos_exp_rate[53] = s_to_rate(0.87f);
+ pos_exp_rate[54] = s_to_rate(1.0f);
+ pos_exp_rate[55] = s_to_rate(1.3f);
+ pos_exp_rate[56] = s_to_rate(1.5f);
+ pos_exp_rate[57] = s_to_rate(1.7f);
+ pos_exp_rate[58] = s_to_rate(2.1f);
+ pos_exp_rate[59] = s_to_rate(2.6f);
+ pos_exp_rate[60] = s_to_rate(3.0f);
+ pos_exp_rate[61] = s_to_rate(3.5f);
+ pos_exp_rate[62] = s_to_rate(4.2f);
+ pos_exp_rate[63] = s_to_rate(5.2f);
+ pos_exp_rate[64] = s_to_rate(5.9f);
+ pos_exp_rate[65] = s_to_rate(6.9f);
+ pos_exp_rate[66] = s_to_rate(8.3f);
+ pos_exp_rate[67] = s_to_rate(10.0f);
+ pos_exp_rate[68] = s_to_rate(12.0f);
+ pos_exp_rate[69] = s_to_rate(14.0f);
+ pos_exp_rate[70] = s_to_rate(17.0f);
+ pos_exp_rate[71] = s_to_rate(21.0f);
+ pos_exp_rate[72] = s_to_rate(24.0f);
+ pos_exp_rate[73] = s_to_rate(28.0f);
+ pos_exp_rate[74] = s_to_rate(33.0f);
+ pos_exp_rate[75] = s_to_rate(42.0f);
+ pos_exp_rate[76] = s_to_rate(48.0f);
+ pos_exp_rate[77] = s_to_rate(55.0f);
+ pos_exp_rate[78] = s_to_rate(67.0f);
+ pos_exp_rate[79] = s_to_rate(83.0f);
+ pos_exp_rate[80] = s_to_rate(95.0f);
+ pos_exp_rate[81] = s_to_rate(111.0f);
+ pos_exp_rate[82] = s_to_rate(133.0f);
+ pos_exp_rate[83] = s_to_rate(166.0f);
+ pos_exp_rate[84] = s_to_rate(190.0f);
+ pos_exp_rate[85] = s_to_rate(222.0f);
+ pos_exp_rate[86] = s_to_rate(266.0f);
+ pos_exp_rate[87] = s_to_rate(333.0f);
+ pos_exp_rate[88] = s_to_rate(380.0f);
+ pos_exp_rate[89] = s_to_rate(444.0f);
+ pos_exp_rate[90] = s_to_rate(532.0f);
+ pos_exp_rate[91] = s_to_rate(666.0f);
+ pos_exp_rate[92] = s_to_rate(760.0f);
+ pos_exp_rate[93] = s_to_rate(888.0f);
+ pos_exp_rate[94] = s_to_rate(1064.0f);
+ pos_exp_rate[95] = s_to_rate(1332.0f);
+ pos_exp_rate[96] = s_to_rate(1520.0f);
+ pos_exp_rate[97] = s_to_rate(1776.0f);
+ pos_exp_rate[98] = s_to_rate(2128.0f);
+ pos_exp_rate[99] = s_to_rate(2664.0f);
+}
+
+
+void spu_device::generate_neg_exp_rate_table()
+{
+ neg_exp_rate[0] = ms_to_rate(0.07f);
+ neg_exp_rate[1] = ms_to_rate(0.09f);
+ neg_exp_rate[2] = ms_to_rate(0.11f);
+ neg_exp_rate[3] = ms_to_rate(0.14f);
+ neg_exp_rate[4] = ms_to_rate(0.18f);
+ neg_exp_rate[5] = ms_to_rate(0.21f);
+ neg_exp_rate[6] = ms_to_rate(0.25f);
+ neg_exp_rate[7] = ms_to_rate(0.31f);
+ neg_exp_rate[8] = ms_to_rate(0.39f);
+ neg_exp_rate[9] = ms_to_rate(0.45f);
+ neg_exp_rate[10] = ms_to_rate(0.53f);
+ neg_exp_rate[11] = ms_to_rate(0.64f);
+ neg_exp_rate[12] = ms_to_rate(0.81f);
+ neg_exp_rate[13] = ms_to_rate(0.93f);
+ neg_exp_rate[14] = ms_to_rate(1.1f);
+ neg_exp_rate[15] = ms_to_rate(1.3f);
+ neg_exp_rate[16] = ms_to_rate(1.6f);
+ neg_exp_rate[17] = ms_to_rate(1.9f);
+ neg_exp_rate[18] = ms_to_rate(2.2f);
+ neg_exp_rate[19] = ms_to_rate(2.6f);
+ neg_exp_rate[20] = ms_to_rate(3.3f);
+ neg_exp_rate[21] = ms_to_rate(3.8f);
+ neg_exp_rate[22] = ms_to_rate(4.4f);
+ neg_exp_rate[23] = ms_to_rate(5.3f);
+ neg_exp_rate[24] = ms_to_rate(6.7f);
+ neg_exp_rate[25] = ms_to_rate(7.6f);
+ neg_exp_rate[26] = ms_to_rate(8.9f);
+ neg_exp_rate[27] = ms_to_rate(11.0f);
+ neg_exp_rate[28] = ms_to_rate(13.0f);
+ neg_exp_rate[29] = ms_to_rate(15.0f);
+ neg_exp_rate[30] = ms_to_rate(18.0f);
+ neg_exp_rate[31] = ms_to_rate(21.0f);
+ neg_exp_rate[32] = ms_to_rate(27.0f);
+ neg_exp_rate[33] = ms_to_rate(31.0f);
+ neg_exp_rate[34] = ms_to_rate(36.0f);
+ neg_exp_rate[35] = ms_to_rate(43.0f);
+ neg_exp_rate[36] = ms_to_rate(53.0f);
+ neg_exp_rate[37] = ms_to_rate(61.0f);
+ neg_exp_rate[38] = ms_to_rate(71.0f);
+ neg_exp_rate[39] = ms_to_rate(86.0f);
+ neg_exp_rate[40] = s_to_rate(0.11f);
+ neg_exp_rate[41] = s_to_rate(0.12f);
+ neg_exp_rate[42] = s_to_rate(0.14f);
+ neg_exp_rate[43] = s_to_rate(0.17f);
+ neg_exp_rate[44] = s_to_rate(0.21f);
+ neg_exp_rate[45] = s_to_rate(0.24f);
+ neg_exp_rate[46] = s_to_rate(0.29f);
+ neg_exp_rate[47] = s_to_rate(0.34f);
+ neg_exp_rate[48] = s_to_rate(0.43f);
+ neg_exp_rate[49] = s_to_rate(0.49f);
+ neg_exp_rate[50] = s_to_rate(0.57f);
+ neg_exp_rate[51] = s_to_rate(0.68f);
+ neg_exp_rate[52] = s_to_rate(0.86f);
+ neg_exp_rate[53] = s_to_rate(0.98f);
+ neg_exp_rate[54] = s_to_rate(1.1f);
+ neg_exp_rate[55] = s_to_rate(1.4f);
+ neg_exp_rate[56] = s_to_rate(1.7f);
+ neg_exp_rate[57] = s_to_rate(2.0f);
+ neg_exp_rate[58] = s_to_rate(2.3f);
+ neg_exp_rate[59] = s_to_rate(2.7f);
+ neg_exp_rate[60] = s_to_rate(3.4f);
+ neg_exp_rate[61] = s_to_rate(3.9f);
+ neg_exp_rate[62] = s_to_rate(4.6f);
+ neg_exp_rate[63] = s_to_rate(5.5f);
+ neg_exp_rate[64] = s_to_rate(6.8f);
+ neg_exp_rate[65] = s_to_rate(7.8f);
+ neg_exp_rate[66] = s_to_rate(9.1f);
+ neg_exp_rate[67] = s_to_rate(11.0f);
+ neg_exp_rate[68] = s_to_rate(14.0f);
+ neg_exp_rate[69] = s_to_rate(16.0f);
+ neg_exp_rate[70] = s_to_rate(18.0f);
+ neg_exp_rate[71] = s_to_rate(22.0f);
+ neg_exp_rate[72] = s_to_rate(27.0f);
+ neg_exp_rate[73] = s_to_rate(31.0f);
+ neg_exp_rate[74] = s_to_rate(36.0f);
+ neg_exp_rate[75] = s_to_rate(44.0f);
+ neg_exp_rate[76] = s_to_rate(55.0f);
+ neg_exp_rate[77] = s_to_rate(63.0f);
+ neg_exp_rate[78] = s_to_rate(73.0f);
+ neg_exp_rate[79] = s_to_rate(88.0f);
+ neg_exp_rate[80] = s_to_rate(109.0f);
+ neg_exp_rate[81] = s_to_rate(125.0f);
+ neg_exp_rate[82] = s_to_rate(146.0f);
+ neg_exp_rate[83] = s_to_rate(175.0f);
+ neg_exp_rate[84] = s_to_rate(219.0f);
+ neg_exp_rate[85] = s_to_rate(250.0f);
+ neg_exp_rate[86] = s_to_rate(292.0f);
+ neg_exp_rate[87] = s_to_rate(350.0f);
+ neg_exp_rate[88] = s_to_rate(438.0f);
+ neg_exp_rate[89] = s_to_rate(500.0f);
+ neg_exp_rate[90] = s_to_rate(584.0f);
+ neg_exp_rate[91] = s_to_rate(700.0f);
+ neg_exp_rate[92] = s_to_rate(876.0f);
+ neg_exp_rate[93] = s_to_rate(1000.0f);
+ neg_exp_rate[94] = s_to_rate(1168.0f);
+ neg_exp_rate[95] = s_to_rate(1400.0f);
+ neg_exp_rate[96] = s_to_rate(1752.0f);
+ neg_exp_rate[97] = s_to_rate(2000.0f);
+ neg_exp_rate[98] = s_to_rate(2336.0f);
+ neg_exp_rate[99] = s_to_rate(2800.0f);
+ neg_exp_rate[100] = s_to_rate(3504.0f);
+ neg_exp_rate[101] = s_to_rate(4000.0f);
+ neg_exp_rate[102] = s_to_rate(4672.0f);
+ neg_exp_rate[103] = s_to_rate(5600.0f);
+ neg_exp_rate[104] = s_to_rate(7008.0f);
+ neg_exp_rate[105] = s_to_rate(8000.0f);
+ neg_exp_rate[106] = s_to_rate(9344.0f);
+ neg_exp_rate[107] = s_to_rate(11200.0f);
+}
+
+void spu_device::generate_decay_rate_table()
+{
+ decay_rate[0] = ms_to_rate(0.07f);
+ decay_rate[1] = ms_to_rate(0.18f);
+ decay_rate[2] = ms_to_rate(0.39f);
+ decay_rate[3] = ms_to_rate(0.81f);
+ decay_rate[4] = ms_to_rate(1.6f);
+ decay_rate[5] = ms_to_rate(3.3f);
+ decay_rate[6] = ms_to_rate(6.7f);
+ decay_rate[7] = ms_to_rate(13.0f);
+ decay_rate[8] = ms_to_rate(27.0f);
+ decay_rate[9] = ms_to_rate(53.0f);
+ decay_rate[10] = s_to_rate(0.11f);
+ decay_rate[11] = s_to_rate(0.21f);
+ decay_rate[12] = s_to_rate(0.43f);
+ decay_rate[13] = s_to_rate(0.86f);
+ decay_rate[14] = s_to_rate(1.7f);
+ decay_rate[15] = s_to_rate(3.4f);
+}
+
+void spu_device::generate_linear_release_rate_table()
+{
+ linear_release_rate[0] = ms_to_rate(0.04f);
+ linear_release_rate[1] = ms_to_rate(0.09f);
+ linear_release_rate[2] = ms_to_rate(0.18f);
+ linear_release_rate[3] = ms_to_rate(0.36f);
+ linear_release_rate[4] = ms_to_rate(0.73f);
+ linear_release_rate[5] = ms_to_rate(1.5f);
+ linear_release_rate[6] = ms_to_rate(2.9f);
+ linear_release_rate[7] = ms_to_rate(5.8f);
+ linear_release_rate[8] = ms_to_rate(12.0f);
+ linear_release_rate[9] = ms_to_rate(23.0f);
+ linear_release_rate[10] = ms_to_rate(46.0f);
+ linear_release_rate[11] = ms_to_rate(93.0f);
+ linear_release_rate[12] = s_to_rate(0.19f);
+ linear_release_rate[13] = s_to_rate(0.37f);
+ linear_release_rate[14] = s_to_rate(1.74f);
+ linear_release_rate[15] = s_to_rate(1.5f);
+ linear_release_rate[16] = s_to_rate(3.0f);
+ linear_release_rate[17] = s_to_rate(5.9f);
+ linear_release_rate[18] = s_to_rate(12.0f);
+ linear_release_rate[19] = s_to_rate(24.0f);
+ linear_release_rate[20] = s_to_rate(48.0f);
+ linear_release_rate[21] = s_to_rate(95.0f);
+ linear_release_rate[22] = s_to_rate(190.0f);
+ linear_release_rate[23] = s_to_rate(380.0f);
+ linear_release_rate[24] = s_to_rate(760.0f);
+ linear_release_rate[25] = s_to_rate(1520.0f);
+ linear_release_rate[26] = s_to_rate(3040.0f);
+}
+
+void spu_device::generate_exp_release_rate_table()
+{
+ exp_release_rate[0] = ms_to_rate(0.07f);
+ exp_release_rate[1] = ms_to_rate(0.18f);
+ exp_release_rate[2] = ms_to_rate(0.39f);
+ exp_release_rate[3] = ms_to_rate(0.81f);
+ exp_release_rate[4] = ms_to_rate(1.6f);
+ exp_release_rate[5] = ms_to_rate(3.3f);
+ exp_release_rate[6] = ms_to_rate(6.7f);
+ exp_release_rate[7] = ms_to_rate(13.0f);
+ exp_release_rate[8] = ms_to_rate(27.0f);
+ exp_release_rate[9] = ms_to_rate(53.0f);
+ exp_release_rate[10] = s_to_rate(0.11f);
+ exp_release_rate[11] = s_to_rate(0.21f);
+ exp_release_rate[12] = s_to_rate(0.43f);
+ exp_release_rate[13] = s_to_rate(0.86f);
+ exp_release_rate[14] = s_to_rate(1.7f);
+ exp_release_rate[15] = s_to_rate(3.4f);
+ exp_release_rate[16] = s_to_rate(6.8f);
+ exp_release_rate[17] = s_to_rate(14.0f);
+ exp_release_rate[18] = s_to_rate(27.0f);
+ exp_release_rate[19] = s_to_rate(55.0f);
+ exp_release_rate[20] = s_to_rate(109.0f);
+ exp_release_rate[21] = s_to_rate(219.0f);
+ exp_release_rate[22] = s_to_rate(438.0f);
+ exp_release_rate[23] = s_to_rate(876.0f);
+ exp_release_rate[24] = s_to_rate(1752.0f);
+ exp_release_rate[25] = s_to_rate(3504.0f);
+ exp_release_rate[26] = s_to_rate(7008.0f);
+}
+
float spu_device::get_linear_rate(const int n)
{
- static constexpr int num_linear_rates=std::size(linear_rate);
+ int num_linear_rates=std::size(linear_rate);
if (n>=num_linear_rates) return 0.0f;
return linear_rate[n]*freq_multiplier;
}
@@ -630,7 +630,7 @@ float spu_device::get_linear_rate_neg_phase(const int n)
float spu_device::get_pos_exp_rate(const int n)
{
- static constexpr int num_pos_exp_rates=std::size(pos_exp_rate);
+ int num_pos_exp_rates=std::size(pos_exp_rate);
if (n>=num_pos_exp_rates) return 0.0f;
return pos_exp_rate[n]*freq_multiplier;
}
@@ -643,7 +643,7 @@ float spu_device::get_pos_exp_rate_neg_phase(const int n)
float spu_device::get_neg_exp_rate(const int n)
{
- static constexpr int num_neg_exp_rates=std::size(neg_exp_rate);
+ int num_neg_exp_rates=std::size(neg_exp_rate);
if (n>=num_neg_exp_rates) return 0.0f;
return -neg_exp_rate[n]*freq_multiplier;
}
@@ -666,14 +666,14 @@ float spu_device::get_sustain_level(const int n)
float spu_device::get_linear_release_rate(const int n)
{
- static constexpr int num_linear_release_rates=std::size(linear_release_rate);
+ int num_linear_release_rates=std::size(linear_release_rate);
if (n>=num_linear_release_rates) return 0.0f;
return linear_release_rate[n]*freq_multiplier;
}
float spu_device::get_exp_release_rate(const int n)
{
- static constexpr int num_exp_release_rates=std::size(exp_release_rate);
+ int num_exp_release_rates=std::size(exp_release_rate);
if (n>=num_exp_release_rates) return 0.0f;
return exp_release_rate[n]*freq_multiplier;
}
diff --git a/src/devices/sound/spureverb.cpp b/src/devices/sound/spureverb.cpp
index e57c33d448d..2bd3c3244bd 100644
--- a/src/devices/sound/spureverb.cpp
+++ b/src/devices/sound/spureverb.cpp
@@ -308,7 +308,7 @@ void spu_device::reverb::process(signed short *output,
signed short *sp=(signed short *)reverb_input,
*dp=(signed short *)output;
- if (rp->band_gain>0.0f)
+ if (rp && rp->band_gain>0.0f)
{
// Do reverb processing
diff --git a/src/devices/sound/ssi263hle.cpp b/src/devices/sound/ssi263hle.cpp
new file mode 100644
index 00000000000..738b6c61084
--- /dev/null
+++ b/src/devices/sound/ssi263hle.cpp
@@ -0,0 +1,230 @@
+// license:BSD-3-Clause
+// copyright-holders:Ryan Holtz
+/***************************************************************************
+
+ Silicon Systems SSI-263A Phoneme Speech Synthesizer
+
+ Temporary implementation using the Votrax SC-01A
+
+ NOTE: This is completely wrong, and exists only to have
+ working audio in Thayer's Quest, which would not otherwise
+ be playable due to relying on speech output for important
+ gameplay cues.
+
+****************************************************************************/
+
+#include "emu.h"
+
+#include "ssi263hle.h"
+
+
+DEFINE_DEVICE_TYPE(SSI263HLE, ssi263hle_device, "ssi263hle", "SSI-263A Speech Synthesizer")
+
+namespace
+{
+
+static const char PHONEME_NAMES[0x40][5] =
+{
+ "PA", "E", "E1", "Y", "YI", "AY", "IE", "I", "A", "AI", "EH", "EH1", "AE", "AE1", "AH", "AH1", "W", "O", "OU", "OO", "IU", "IU1", "U", "U1", "UH", "UH1", "UH2", "UH3", "ER", "R", "R1", "R2",
+ "L", "L1", "LF", "W", "B", "D", "KV", "P", "T", "K", "HV", "HVC", "HF", "HFC", "HN", "Z", "S", "J", "SCH", "V", "F", "THV", "TH", "M", "N", "NG", ":A", ":OH", ":U", ":UH", "E2", "LB"
+};
+
+static const u8 PHONEMES_TO_SC01[0x40] =
+{
+ 0x03, 0x2c, 0x3b, 0x3c, 0x22, 0x21, 0x29, 0x27, 0x20, 0x05, 0x01, 0x00, 0x2e, 0x2f, 0x15, 0x15,
+ 0x13, 0x26, 0x35, 0x17, 0x36, 0x16, 0x28, 0x37, 0x32, 0x32, 0x31, 0x23, 0x3a, 0x2b, 0x2b, 0x2b,
+ 0x18, 0x18, 0x18, 0x2d, 0x0e, 0x1e, 0x1c, 0x25, 0x2a, 0x19, 0x03, 0x03, 0x1b, 0x03, 0x03, 0x12,
+ 0x1f, 0x07, 0x11, 0x0f, 0x1d, 0x38, 0x39, 0x0c, 0x0d, 0x14, 0x08, 0x34, 0x28, 0x37, 0x02, 0x18
+};
+
+} // anonymous namespace
+
+ssi263hle_device::ssi263hle_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, SSI263HLE, tag, owner, clock)
+ , device_mixer_interface(mconfig, *this, 1)
+ , m_votrax(*this, "votrax")
+ , m_ar_cb(*this)
+ , m_phoneme_timer(nullptr)
+ , m_duration(0)
+ , m_phoneme(0)
+ , m_inflection(0)
+ , m_rate(0)
+ , m_articulation(0)
+ , m_control(false)
+ , m_amplitude(0)
+ , m_filter(0)
+ , m_mode(0)
+ , m_data_request(1)
+ , m_votrax_fifo_wr(0)
+ , m_votrax_fifo_rd(0)
+ , m_votrax_fifo_cnt(0)
+{
+}
+
+void ssi263hle_device::device_start()
+{
+ m_phoneme_timer = timer_alloc(FUNC(ssi263hle_device::phoneme_tick), this);
+
+ save_item(NAME(m_duration));
+ save_item(NAME(m_phoneme));
+ save_item(NAME(m_inflection));
+ save_item(NAME(m_rate));
+ save_item(NAME(m_articulation));
+ save_item(NAME(m_control));
+ save_item(NAME(m_amplitude));
+ save_item(NAME(m_filter));
+ save_item(NAME(m_mode));
+
+ save_item(NAME(m_votrax_fifo));
+ save_item(NAME(m_votrax_fifo_wr));
+ save_item(NAME(m_votrax_fifo_rd));
+ save_item(NAME(m_votrax_fifo_cnt));
+}
+
+void ssi263hle_device::device_reset()
+{
+ m_phoneme_timer->adjust(attotime::never);
+
+ m_duration = 0;
+ m_phoneme = 0;
+ m_inflection = 0;
+ m_rate = 0;
+ m_articulation = 0;
+ m_control = false;
+ m_amplitude = 0;
+ m_filter = 0;
+ m_mode = 0;
+
+ m_data_request = 1;
+
+ std::fill(std::begin(m_votrax_fifo), std::end(m_votrax_fifo), 0);
+ m_votrax_fifo_wr = 0;
+ m_votrax_fifo_rd = 0;
+ m_votrax_fifo_cnt = 0;
+}
+
+void ssi263hle_device::map(address_map &map)
+{
+ map(0x00, 0x00).rw(FUNC(ssi263hle_device::status_r), FUNC(ssi263hle_device::duration_phoneme_w));
+ map(0x01, 0x01).rw(FUNC(ssi263hle_device::status_r), FUNC(ssi263hle_device::inflection_w));
+ map(0x02, 0x02).rw(FUNC(ssi263hle_device::status_r), FUNC(ssi263hle_device::rate_inflection_w));
+ map(0x03, 0x03).rw(FUNC(ssi263hle_device::status_r), FUNC(ssi263hle_device::control_articulation_amplitude_w));
+ map(0x04, 0x07).rw(FUNC(ssi263hle_device::status_r), FUNC(ssi263hle_device::filter_frequency_w));
+}
+
+void ssi263hle_device::device_add_mconfig(machine_config &config)
+{
+ VOTRAX_SC01(config, m_votrax, DERIVED_CLOCK(1, 1));
+ m_votrax->ar_callback().set(FUNC(ssi263hle_device::votrax_request));
+ m_votrax->add_route(ALL_OUTPUTS, *this, 1.0, AUTO_ALLOC_INPUT, 0);
+}
+
+TIMER_CALLBACK_MEMBER(ssi263hle_device::phoneme_tick)
+{
+ m_data_request = 0;
+ m_ar_cb(m_data_request);
+}
+
+void ssi263hle_device::duration_phoneme_w(u8 data)
+{
+ const int frame_time = ((4096 * (16 - m_rate)) / 2); // microseconds, should actually be derived from our clock, but this way we get microseconds directly
+ const int phoneme_time = frame_time * (4 - m_duration); // microseconds
+
+ m_duration = (data >> 5) & 0x03;
+ m_phoneme = data & 0x3f;
+
+ m_data_request = 1;
+ m_ar_cb(m_data_request);
+
+ switch (m_mode)
+ {
+ case 0:
+ case 1:
+ // phoneme timing response
+ m_phoneme_timer->adjust(attotime::from_usec(phoneme_time));
+ break;
+ case 2:
+ // frame timing response
+ m_phoneme_timer->adjust(attotime::from_usec(frame_time));
+ break;
+ case 3:
+ // disable A/_R output
+ break;
+ }
+
+ if (m_phoneme)
+ {
+ if (m_votrax_fifo_cnt < std::size(m_votrax_fifo))
+ {
+ m_votrax_fifo[m_votrax_fifo_wr] = PHONEMES_TO_SC01[m_phoneme];
+ if (m_votrax_fifo_cnt == 0)
+ {
+ m_votrax->write(PHONEMES_TO_SC01[m_phoneme]);
+ }
+ m_votrax_fifo_wr = (m_votrax_fifo_wr + 1) % std::size(m_votrax_fifo);
+ m_votrax_fifo_cnt++;
+ }
+ }
+}
+
+void ssi263hle_device::inflection_w(u8 data)
+{
+ m_inflection &= 0x807;
+ m_inflection |= data << 3;
+}
+
+void ssi263hle_device::rate_inflection_w(u8 data)
+{
+ m_inflection &= 0x7f8;
+ m_inflection |= (BIT(data, 3) << 11) | (data & 0x07);
+ m_rate = data >> 4;
+ m_votrax->inflection_w(1);
+}
+
+void ssi263hle_device::control_articulation_amplitude_w(u8 data)
+{
+ if (m_control && !BIT(data, 7))
+ {
+ m_mode = m_duration;
+ }
+
+ m_control = BIT(data, 7);
+ m_articulation = (data >> 4) & 0x07;
+ m_amplitude = data & 0x0f;
+}
+
+void ssi263hle_device::filter_frequency_w(u8 data)
+{
+ m_filter = data;
+}
+
+u8 ssi263hle_device::status_r()
+{
+ // D7 is an output for the inverted state of A/_R. Register address bits are ignored.
+ return BIT(~m_data_request, 0) << 7;
+}
+
+void ssi263hle_device::votrax_request(int state)
+{
+ if (m_votrax_fifo_cnt == 0 || state != ASSERT_LINE)
+ {
+ return;
+ }
+
+ m_votrax_fifo_cnt--;
+ const u8 previous_phoneme = m_votrax_fifo[m_votrax_fifo_rd];
+ m_votrax_fifo_rd = (m_votrax_fifo_rd + 1) % std::size(m_votrax_fifo);
+ if (m_votrax_fifo_cnt == 0)
+ {
+ if (previous_phoneme != 0x3f)
+ {
+ m_votrax_fifo[m_votrax_fifo_wr] = 0x3f;
+ m_votrax_fifo_wr = (m_votrax_fifo_wr + 1) % std::size(m_votrax_fifo);
+ m_votrax_fifo_cnt++;
+ m_votrax->write(0x3f);
+ }
+ return;
+ }
+
+ m_votrax->write(m_votrax_fifo[m_votrax_fifo_rd]);
+}
diff --git a/src/devices/sound/ssi263hle.h b/src/devices/sound/ssi263hle.h
new file mode 100644
index 00000000000..48b4a8ccb18
--- /dev/null
+++ b/src/devices/sound/ssi263hle.h
@@ -0,0 +1,76 @@
+// license:BSD-3-Clause
+// copyright-holders:Ryan Holtz
+/**********************************************************************
+
+ Silicon Systems SSI-263A Phoneme Speech Synthesizer
+
+ Temporary implementation using the Votrax SC-01A
+
+ NOTE: This is completely wrong, and exists only to have
+ working audio in Thayer's Quest, which would not otherwise
+ be playable due to relying on speech output for important
+ gameplay cues.
+
+**********************************************************************/
+
+#ifndef MAME_SOUND_SSI263HLE_H
+#define MAME_SOUND_SSI263HLE_H
+
+#pragma once
+
+#include "sound/votrax.h"
+
+class ssi263hle_device : public device_t, public device_mixer_interface
+{
+public:
+ static constexpr feature_type imperfect_features() { return feature::SOUND; }
+
+ ssi263hle_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0);
+
+ void map(address_map &map);
+
+ auto ar_callback() { return m_ar_cb.bind(); }
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_add_mconfig(machine_config &config) override;
+
+private:
+ required_device<votrax_sc01_device> m_votrax;
+
+ TIMER_CALLBACK_MEMBER(phoneme_tick);
+
+ void duration_phoneme_w(u8 data);
+ void inflection_w(u8 data);
+ void rate_inflection_w(u8 data);
+ void control_articulation_amplitude_w(u8 data);
+ void filter_frequency_w(u8 data);
+ u8 status_r();
+
+ void votrax_request(int state);
+
+ devcb_write_line m_ar_cb;
+
+ emu_timer *m_phoneme_timer = nullptr;
+
+ u8 m_duration;
+ u8 m_phoneme;
+ u16 m_inflection;
+ u8 m_rate;
+ u8 m_articulation;
+ bool m_control;
+ u8 m_amplitude;
+ u8 m_filter;
+ u8 m_mode;
+ u8 m_data_request;
+
+ u8 m_votrax_fifo[1024];
+ u32 m_votrax_fifo_wr;
+ u32 m_votrax_fifo_rd;
+ u32 m_votrax_fifo_cnt;
+};
+
+DECLARE_DEVICE_TYPE(SSI263HLE, ssi263hle_device)
+
+#endif
diff --git a/src/devices/sound/stt_sa1.cpp b/src/devices/sound/stt_sa1.cpp
new file mode 100644
index 00000000000..efd9e49e163
--- /dev/null
+++ b/src/devices/sound/stt_sa1.cpp
@@ -0,0 +1,190 @@
+// license:BSD-3-Clause
+// copyright-holders:windyfairy
+/*
+ ST-Techno STT-SA1 PCM sound chip
+ Originally implemented in an FPGA
+*/
+#include "emu.h"
+#include "stt_sa1.h"
+
+// #define VERBOSE (LOG_GENERAL)
+// #define LOG_OUTPUT_STREAM std::cout
+#include "logmacro.h"
+
+
+DEFINE_DEVICE_TYPE(STT_SA1, stt_sa1_device, "stt_sa1", "ST-Techno STT-SA1 Sound")
+
+stt_sa1_device::stt_sa1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, STT_SA1, tag, owner, clock)
+ , device_sound_interface(mconfig, *this)
+ , device_rom_interface(mconfig, *this)
+ , m_stream(nullptr)
+{
+}
+
+void stt_sa1_device::enable_w(uint16_t data)
+{
+ m_enabled = data != 0;
+}
+
+uint16_t stt_sa1_device::read(offs_t offset, uint16_t mem_mask)
+{
+ offset &= 0x7f;
+
+ return m_regs[offset]; // TODO: Should this return addr_cur for regs 1 and 2 instead?
+}
+
+void stt_sa1_device::write(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ offset &= 0x7f;
+ const int v = offset >> 4;
+ const int reg = offset & 0xf;
+
+ m_regs[offset] = data;
+
+ switch (reg) {
+ case 1:
+ m_voice[v].addr_start = m_voice[v].addr_cur = (m_voice[v].addr_cur & 0xffff0000000) | (uint64_t(data) << 12);
+ LOG("voice %d: start = %08llx\n", v, m_voice[v].addr_cur >> 12);
+ break;
+ case 2:
+ m_voice[v].addr_start = m_voice[v].addr_cur = (m_voice[v].addr_cur & 0x0000ffff000) | (uint64_t(data & 0x7fff) << 28);
+ m_voice[v].is_looped = BIT(data, 15) != 0;
+ LOG("voice %d: start = %08llx, is_looped = %d\n", v, m_voice[v].addr_cur >> 12, m_voice[v].is_looped);
+ break;
+
+ case 3:
+ m_voice[v].freq = data;
+ LOG("voice %d: step = %08x\n", v, m_voice[v].freq);
+ break;
+
+ case 4:
+ m_voice[v].addr_start = (m_voice[v].addr_start & 0xffff0000000) | (uint64_t(data) << 12);
+ LOG("voice %d: addr_start = %08llx\n", v, m_voice[v].addr_start >> 12);
+ break;
+ case 5:
+ m_voice[v].addr_start = (m_voice[v].addr_start & 0x0000ffff000) | (uint64_t(data) << 28);
+ LOG("voice %d: addr_start = %08llx\n", v, m_voice[v].addr_start >> 12);
+ break;
+
+ case 6:
+ m_voice[v].addr_end = (m_voice[v].addr_end & 0xffff0000000) | (uint64_t(data) << 12);
+ LOG("voice %d: addr_end = %08llx\n", v, m_voice[v].addr_end >> 12);
+ break;
+ case 7:
+ m_voice[v].addr_end = (m_voice[v].addr_end & 0x0000ffff000) | (uint64_t(data) << 28);
+ LOG("voice %d: addr_end = %08llx\n", v, m_voice[v].addr_end >> 12);
+ break;
+
+ case 0x0b:
+ m_voice[v].vol_l = data;
+ LOG("voice %d: vol_l = %08x\n", v, m_voice[v].vol_l);
+ break;
+ case 0x0c:
+ m_voice[v].vol_r = data;
+ LOG("voice %d: vol_r = %08x\n", v, m_voice[v].vol_r);
+ break;
+
+ default:
+ LOG("Unknown register usage: voice %d, register %x, data %04x\n", v, reg, data);
+ break;
+ }
+}
+
+uint16_t stt_sa1_device::key_r()
+{
+ if (!m_enabled)
+ return 0;
+
+ if (!machine().side_effects_disabled())
+ m_stream->update();
+
+ return m_keyctrl;
+}
+
+void stt_sa1_device::key_w(uint16_t data)
+{
+ if (!m_enabled)
+ return;
+
+ const u16 prev = m_keyctrl;
+
+ m_stream->update();
+
+ m_keyctrl = data;
+
+ for (int v = 0; v < 8; v++) {
+ if (BIT(m_keyctrl, v) && !BIT(prev, v)) {
+ // keyon
+ m_voice[v].enabled = true;
+ m_voice[v].addr_cur = m_voice[v].addr_start;
+ } else if (!BIT(m_keyctrl, v) && BIT(prev, v)) {
+ // keyoff
+ m_voice[v].enabled = false;
+ }
+ }
+}
+
+void stt_sa1_device::device_start()
+{
+ m_stream = stream_alloc(0, 2, clock() / 448);
+
+ save_item(STRUCT_MEMBER(m_voice, addr_start));
+ save_item(STRUCT_MEMBER(m_voice, addr_cur));
+ save_item(STRUCT_MEMBER(m_voice, addr_end));
+ save_item(STRUCT_MEMBER(m_voice, vol_l));
+ save_item(STRUCT_MEMBER(m_voice, vol_r));
+ save_item(STRUCT_MEMBER(m_voice, freq));
+ save_item(STRUCT_MEMBER(m_voice, is_looped));
+ save_item(STRUCT_MEMBER(m_voice, enabled));
+ save_item(NAME(m_keyctrl));
+ save_item(NAME(m_regs));
+ save_item(NAME(m_enabled));
+}
+
+void stt_sa1_device::device_reset()
+{
+ m_enabled = false;
+
+ std::fill(std::begin(m_regs), std::end(m_regs), 0);
+
+ for (int i = 0; i < 8; i++) {
+ m_voice[i].addr_start = 0;
+ m_voice[i].addr_cur = 0;
+ m_voice[i].addr_end = 0;
+ m_voice[i].vol_l = 0;
+ m_voice[i].vol_r = 0;
+ m_voice[i].freq = 0;
+ m_voice[i].is_looped = false;
+ m_voice[i].enabled = false;
+ }
+}
+
+void stt_sa1_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
+{
+ outputs[0].fill(0);
+ outputs[1].fill(0);
+
+ for (int v = 0; v < 8; v++) {
+ voice_t &voice = m_voice[v];
+
+ for (int i = 0; i < outputs[0].samples() && voice.enabled; i++) {
+ const offs_t offset = voice.addr_cur >> 12;
+ const int sample = s8(read_byte(offset)) << 8;
+
+ voice.addr_cur += voice.freq;
+
+ outputs[0].add_int(i, (sample * voice.vol_l) >> 16, 32768 * 8);
+ outputs[1].add_int(i, (sample * voice.vol_r) >> 16, 32768 * 8);
+
+ if (voice.addr_cur >= voice.addr_end) {
+ if (!voice.is_looped) {
+ voice.enabled = false;
+ m_keyctrl &= ~(1 << v);
+ } else {
+ voice.addr_cur = voice.addr_start;
+ }
+ }
+ }
+ }
+}
diff --git a/src/devices/sound/stt_sa1.h b/src/devices/sound/stt_sa1.h
new file mode 100644
index 00000000000..8436c2f7f6c
--- /dev/null
+++ b/src/devices/sound/stt_sa1.h
@@ -0,0 +1,57 @@
+// license:BSD-3-Clause
+// copyright-holders:windyfairy
+#ifndef MAME_SOUND_STT_SA1_H
+#define MAME_SOUND_STT_SA1_H
+
+#pragma once
+
+#include "dirom.h"
+
+class stt_sa1_device : public device_t,
+ public device_sound_interface,
+ public device_rom_interface<24, 1, 0, ENDIANNESS_LITTLE>
+{
+public:
+ static constexpr feature_type imperfect_features() { return feature::SOUND; } // unemulated and/or unverified effects and envelopes
+
+ stt_sa1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ void enable_w(uint16_t data);
+
+ uint16_t read(offs_t offset, uint16_t mem_mask = ~0);
+ void write(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
+
+ uint16_t key_r();
+ void key_w(uint16_t data);
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
+
+private:
+ struct voice_t {
+ uint64_t addr_start;
+ uint64_t addr_end;
+ uint64_t addr_cur;
+ uint16_t vol_l;
+ uint16_t vol_r;
+ uint16_t freq;
+ bool is_looped;
+ bool enabled;
+ };
+
+ sound_stream *m_stream;
+
+ voice_t m_voice[8];
+ uint16_t m_keyctrl; // Key on/off control bit
+
+ uint16_t m_regs[128];
+
+ bool m_enabled;
+};
+
+DECLARE_DEVICE_TYPE(STT_SA1, stt_sa1_device)
+
+#endif // MAME_SOUND_STT_SA1_H
diff --git a/src/devices/sound/swp00.cpp b/src/devices/sound/swp00.cpp
index f94006a1868..119099526b3 100644
--- a/src/devices/sound/swp00.cpp
+++ b/src/devices/sound/swp00.cpp
@@ -6,6 +6,66 @@
#include "emu.h"
#include "swp00.h"
+/*
+
+ Used in the MU50, the SWP00 is the combination of a rompler called
+ AWM2 (Advanced Wave Memory 2) and an effects DSP called MEG
+ (Multiple Effects Generator). It is the simpler variant of those, a
+ simplification and integration of the SWP20/SWD/MEG/EQ combo use in
+ the MU80.
+
+ Its clock is 33.9MHz and the output is at 44100Hz stereo (768 cycles
+ per sample pair) per dac output.
+
+
+ AWM2:
+
+ The AWM2 is in charge of handling the individual channels. It
+ manages reading the rom, decoding the samples, applying volume and
+ envelopes and lfos and filtering the result. The channels are
+ volume-modulated and summed into 7 outputs which are then processed
+ by the MEG.
+
+ As all the SWPs, the sound data can be four formats (8 bits, 12
+ bits, 16 bits, and a 8-bits log format with roughly 10 bits of
+ dynamic). It's interesting to note that the 8-bits format is not
+ used by the MU50. The rom bus is 24 bits address and 8 bits data
+ wide. It applies a single, Chamberlin-configuration LPF to the
+ sample data. Envelopes are handled semi-automatically, and the
+ final result volume-modulated (global volume, pan, tremolo, dispatch
+ in dry/reverb/chorus/variation) in 7 output channels.
+
+
+ MEG:
+
+ The MEG in this case is an internal DSP with a fixed program in four
+ selectable variants. It has 192 steps of program, and can issue a
+ memory access to the effects DRAM every 3 cycles. The programs are
+ internal and as far as we know not dumpable. We managed a
+ reimplementation though.
+
+ The program does the effects "reverb", "chorus" and "variation" and
+ mixing between all those. The four variants only in practice impact
+ the variation segment, in addresses 109-191 roughly.
+
+ Each instruction is associated with a dynamically changeable 10-bit
+ constant used as a fixed point value (either 1.9 or 3.7 depending on
+ the instruction). Every third instruction (pc multiple of 3) is
+ also associated with a 16-bits offset for the potential memory
+ access.
+
+
+ Interface:
+
+ The interface is 8-bits wide but would have wanted to be 16-bits, with
+ 11 address bits. There are three address formats depending on the
+ part of the chip one speaks to:
+ 000 0sss ssss Global controls
+ 001 1ppp pppl MEG, offsets (16-bits values, l=high/low byte, pc 00-bd, divided by 3)
+ 01p pppp pppl MEG, constants (16-bits values, l=high/low byte, pc 00-bf)
+ sss sscc cccs AWM2, channel/slot combination (slot = 8-b and 20-37)
+*/
+
DEFINE_DEVICE_TYPE(SWP00, swp00_device, "swp00", "Yamaha SWP00 (TC170C120SF / XQ036A00) sound chip")
swp00_device::swp00_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
@@ -19,86 +79,1764 @@ void swp00_device::device_add_mconfig(machine_config &config)
{
}
+const std::array<u32, 4> swp00_device::lfo_shape_centered_saw = { 0x00000000, 0x00000000, 0xfff00000, 0xfff00000 }; // --////--
+const std::array<u32, 4> swp00_device::lfo_shape_centered_tri = { 0x00000000, 0x0007ffff, 0xfff7ffff, 0xfff00000 }; // --/\/\--
+const std::array<u32, 4> swp00_device::lfo_shape_offset_saw = { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; // __////__
+const std::array<u32, 4> swp00_device::lfo_shape_offset_tri = { 0x00000000, 0x00000000, 0x000fffff, 0x000fffff }; // __/\/\__
+
+const std::array<s32, 16> swp00_device::panmap = {
+ 0x000, 0x040, 0x080, 0x0c0,
+ 0x100, 0x140, 0x180, 0x1c0,
+ 0x200, 0x240, 0x280, 0x2c0,
+ 0x300, 0x340, 0x380, 0xfff
+};
+
+bool swp00_device::istep(s32 &value, s32 limit, s32 step)
+{
+ // fprintf(stderr, "istep(%x, %x, %x)\n", value, limit, step);
+ if(value < limit) {
+ value += step;
+ if(value >= limit) {
+ value = limit;
+ return true;
+ }
+ return false;
+ }
+
+ if(value > limit) {
+ value -= step;
+ if(value <= limit) {
+ value = limit;
+ return true;
+ }
+ return false;
+ }
+
+ return true;
+}
+
+s32 swp00_device::fpadd(s32 value, s32 step)
+{
+ s32 e = value >> 24;
+ s32 m = value & 0xffffff;
+
+ m += step << e;
+ if(m & 0xfe000000)
+ return 0xfffffff;
+
+ while(m & 0x01000000) {
+ m <<= 1;
+ e ++;
+ }
+ if(e >= 16)
+ return 0xfffffff;
+ return (e << 24) | (m & 0xffffff);
+}
+
+s32 swp00_device::fpsub(s32 value, s32 step)
+{
+ s32 e = value >> 24;
+ s32 m = (value & 0xffffff) | 0xfe000000;
+ m = e < 0xc ? m - (step << e) : (m >> (e - 0xb)) - (step << 0xb);
+ if(m >= 0)
+ return 0;
+ if(e >= 0xc)
+ e = 0xb;
+ while(m < 0xfe000000) {
+ if(!e)
+ return 0;
+ e --;
+ m >>= 1;
+ }
+ while(e != 0xf && (m >= 0xff000000)) {
+ e ++;
+ m <<= 1;
+ }
+
+ return (e << 24) | (m & 0xffffff);
+}
+
+bool swp00_device::fpstep(s32 &value, s32 limit, s32 step)
+{
+ // value, limit and step are 4.24 but step has its exponent and
+ // top four bits zero
+
+ if(value == limit)
+ return true;
+ if(value < limit) {
+ value = fpadd(value, step);
+ if(value >= limit) {
+ value = limit;
+ return true;
+ }
+ return false;
+ }
+
+ value = fpsub(value, step);
+ if(value <= limit) {
+ value = limit;
+ return true;
+ }
+ return false;
+}
+
+// sample is signed 24.8
+s32 swp00_device::fpapply(s32 value, s32 sample)
+{
+ if(value >= 0x10000000)
+ return 0;
+ return (s64(sample) - ((s64(sample) * ((value >> 9) & 0x7fff)) >> 16)) >> (value >> 24);
+}
+
+// sample is signed 24.8
+s32 swp00_device::lpffpapply(s32 value, s32 sample)
+{
+ return ((((value >> 7) & 0x7fff) | 0x8000) * s64(sample)) >> (31 - (value >> 22));
+}
+
+// Some tables we need. Maybe they're in roms inside the chip,
+// maybe they're logic. Probably slightly inexact too, would need
+// a complicated hardware setup to really test them.
+
+const std::array<s32, 0x80> swp00_device::attack_linear_step = {
+ 0x00027, 0x0002b, 0x0002f, 0x00033, 0x00037, 0x0003d, 0x00042, 0x00048,
+ 0x0004d, 0x00056, 0x0005e, 0x00066, 0x0006f, 0x0007a, 0x00085, 0x00090,
+ 0x0009b, 0x000ac, 0x000bd, 0x000cc, 0x000de, 0x000f4, 0x00109, 0x00120,
+ 0x00135, 0x00158, 0x00179, 0x00199, 0x001bc, 0x001e7, 0x00214, 0x00240,
+ 0x0026b, 0x002af, 0x002f2, 0x00332, 0x00377, 0x003d0, 0x0042c, 0x00480,
+ 0x004dc, 0x0055e, 0x005e9, 0x0066e, 0x006f4, 0x007a4, 0x00857, 0x0090b,
+ 0x009c3, 0x00acb, 0x00bd6, 0x00ce6, 0x00e00, 0x00f5e, 0x010d2, 0x01234,
+ 0x0139e, 0x015d0, 0x017f3, 0x01a20, 0x01c4a, 0x01f52, 0x02232, 0x0250f,
+ 0x027ff, 0x02c72, 0x03109, 0x0338b, 0x039c4, 0x04038, 0x04648, 0x04c84,
+ 0x05262, 0x05c1c, 0x065af, 0x06f5c, 0x07895, 0x0866f, 0x09470, 0x0a19e,
+ 0x0ae4c, 0x0c566, 0x0db8d, 0x0f00f, 0x10625, 0x12937, 0x14954, 0x16c17,
+ 0x1886e, 0x1c71c, 0x20000, 0x239e1, 0x2647c, 0x2aaab, 0x2ecfc, 0x3241f,
+ 0x35e51, 0x3a83b, 0x40000, 0x4325c, 0x47dc1, 0x4c8f9, 0x50505, 0x55555,
+ 0x58160, 0x5d174, 0x60606, 0x62b2e, 0x67b24, 0x6a63c, 0x6d3a0, 0x6eb3e,
+ 0x71c72, 0x73616, 0x75075, 0x76b98, 0x78788, 0x78788, 0x7a44c, 0x7a44c,
+ 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c,
+};
+
+const std::array<s32, 0x20> swp00_device::decay_linear_step = {
+ 0x15083, 0x17ad2, 0x1a41a, 0x1cbe7, 0x1f16d, 0x22ef1, 0x26a44, 0x2a1e4,
+ 0x2da35, 0x34034, 0x3a197, 0x40000, 0x45b82, 0x4b809, 0x51833, 0x57262,
+ 0x5d9f7, 0x6483f, 0x6b15c, 0x71c72, 0x77976, 0x7d119, 0x83127, 0x88889,
+ 0x8d3dd, 0x939a8, 0x991f2, 0x9d89e, 0xa0a0a, 0xa57eb, 0xa72f0, 0xac769,
+};
+
void swp00_device::device_start()
{
m_stream = stream_alloc(0, 2, 44100);
+
+ save_item(NAME(m_waverom_access));
+ save_item(NAME(m_waverom_val));
+ save_item(NAME(m_meg_control));
+
+ save_item(NAME(m_buffer_offset));
+ save_item(NAME(m_rev_vol));
+ save_item(NAME(m_cho_vol));
+ save_item(NAME(m_var_vol));
+
+ save_item(NAME(m_var_lfo_phase));
+ save_item(NAME(m_var_lfo_h_1));
+ save_item(NAME(m_var_lfo_h_2));
+ save_item(NAME(m_var_lfo1a));
+ save_item(NAME(m_var_lfo2a));
+ save_item(NAME(m_var_lfo3a));
+ save_item(NAME(m_var_lfo4a));
+
+ save_item(NAME(m_var_filter_1));
+ save_item(NAME(m_var_filter_2));
+ save_item(NAME(m_var_filter_3));
+
+ save_item(NAME(m_var_filter2_1));
+ save_item(NAME(m_var_filter2_2a));
+ save_item(NAME(m_var_filter2_2b));
+ save_item(NAME(m_var_filter2_3a));
+ save_item(NAME(m_var_filter2_3b));
+ save_item(NAME(m_var_filter2_4));
+
+ save_item(NAME(m_var_filterp_l_1));
+ save_item(NAME(m_var_filterp_l_2));
+ save_item(NAME(m_var_filterp_l_3));
+ save_item(NAME(m_var_filterp_l_4));
+ save_item(NAME(m_var_filterp_l_5));
+ save_item(NAME(m_var_filterp_l_6));
+ save_item(NAME(m_var_filterp_r_1));
+ save_item(NAME(m_var_filterp_r_2));
+ save_item(NAME(m_var_filterp_r_3));
+ save_item(NAME(m_var_filterp_r_4));
+ save_item(NAME(m_var_filterp_r_5));
+ save_item(NAME(m_var_filterp_r_6));
+
+ save_item(NAME(m_var_filter3_1));
+ save_item(NAME(m_var_filter3_2));
+
+ save_item(NAME(m_var_h1));
+ save_item(NAME(m_var_h2));
+ save_item(NAME(m_var_h3));
+ save_item(NAME(m_var_h4));
+
+ save_item(NAME(m_cho_lfo_phase));
+ save_item(NAME(m_cho_filter_l_1));
+ save_item(NAME(m_cho_filter_l_2));
+ save_item(NAME(m_cho_filter_l_3));
+ save_item(NAME(m_cho_filter_r_1));
+ save_item(NAME(m_cho_filter_r_2));
+ save_item(NAME(m_cho_filter_r_3));
+
+ save_item(NAME(m_rev_filter_1));
+ save_item(NAME(m_rev_filter_2));
+ save_item(NAME(m_rev_filter_3));
+ save_item(NAME(m_rev_hist_a));
+ save_item(NAME(m_rev_hist_b));
+ save_item(NAME(m_rev_hist_c));
+ save_item(NAME(m_rev_hist_d));
+
+ save_item(NAME(m_rev_buffer));
+ save_item(NAME(m_cho_buffer));
+ save_item(NAME(m_var_buffer));
+ save_item(NAME(m_offset));
+ save_item(NAME(m_const));
+ save_item(NAME(m_lpf_info));
+ save_item(NAME(m_lpf_speed));
+ save_item(NAME(m_lfo_famod_depth));
+ save_item(NAME(m_rev_level));
+ save_item(NAME(m_dry_level));
+ save_item(NAME(m_cho_level));
+ save_item(NAME(m_var_level));
+ save_item(NAME(m_glo_level));
+ save_item(NAME(m_panning));
+ save_item(NAME(m_attack_speed));
+ save_item(NAME(m_attack_level));
+ save_item(NAME(m_decay_speed));
+ save_item(NAME(m_decay_level));
+ save_item(NAME(m_pitch));
+ save_item(NAME(m_sample_start));
+ save_item(NAME(m_sample_end));
+ save_item(NAME(m_sample_dec_and_format));
+ save_item(NAME(m_sample_address));
+ save_item(NAME(m_lfo_step));
+ save_item(NAME(m_lfo_pmod_depth));
+
+ save_item(NAME(m_lfo_phase));
+ save_item(NAME(m_sample_pos));
+ save_item(NAME(m_envelope_level));
+
+ save_item(NAME(m_glo_level_cur));
+ save_item(NAME(m_pan_l));
+ save_item(NAME(m_pan_r));
+
+ save_item(NAME(m_lpf_feedback));
+ save_item(NAME(m_lpf_target_value));
+ save_item(NAME(m_lpf_value));
+ save_item(NAME(m_lpf_timer));
+ save_item(NAME(m_lpf_ha));
+ save_item(NAME(m_lpf_hb));
+
+ save_item(NAME(m_active));
+ save_item(NAME(m_decay));
+ save_item(NAME(m_decay_done));
+ save_item(NAME(m_lpf_done));
+
+ save_item(NAME(m_dpcm_current));
+ save_item(NAME(m_dpcm_next));
+ save_item(NAME(m_dpcm_address));
+
+ for(int i=0; i<128; i++) {
+ u32 v = 0;
+ switch(i >> 3) {
+ default: v = ((i & 7) + 8) << (1 + (i >> 3)); break;
+ case 0xb: v = ((i & 7) + 4) << 13; break;
+ case 0xc: v = ((i & 6) + 6) << 14; break;
+ case 0xd: v = ((i & 4) + 7) << 15; break;
+ case 0xe: v = 15 << 15; break;
+ case 0xf: v = 31 << 15; break;
+ }
+ m_global_step[i] = v;
+ }
+
+ // Delta-packed samples decompression.
+
+ for(int i=0; i<128; i++) {
+ s16 base = ((i & 0x1f) << (5+(i >> 5))) + (((1 << (i >> 5))-1) << 10);
+ m_dpcm[i | 0x80] = - base;
+ m_dpcm[i] = + base;
+ }
}
void swp00_device::device_reset()
{
+ m_waverom_access = 0;
+ m_waverom_val = 0;
+ m_meg_control = 0;
+
+ m_buffer_offset = 0;
+ m_rev_vol = 0;
+ m_cho_vol = 0;
+ m_var_vol = 0;
+
+ m_var_lfo_phase = 0;
+ m_var_lfo_h_1 = 0;
+ m_var_lfo_h_2 = 0;
+ m_var_lfo1a = 0;
+ m_var_lfo2a = 0;
+ m_var_lfo3a = 0;
+ m_var_lfo4a = 0;
+ m_var_filter_1 = 0;
+ m_var_filter_2 = 0;
+ m_var_filter_3 = 0;
+ m_var_filter2_1 = 0;
+ m_var_filter2_2a = 0;
+ m_var_filter2_2b = 0;
+ m_var_filter2_3a = 0;
+ m_var_filter2_3b = 0;
+ m_var_filter2_4 = 0;
+ m_var_filter3_1 = 0;
+ m_var_filter3_2 = 0;
+ m_var_filterp_l_1 = 0;
+ m_var_filterp_l_2 = 0;
+ m_var_filterp_l_3 = 0;
+ m_var_filterp_l_4 = 0;
+ m_var_filterp_l_5 = 0;
+ m_var_filterp_l_6 = 0;
+ m_var_filterp_r_1 = 0;
+ m_var_filterp_r_2 = 0;
+ m_var_filterp_r_3 = 0;
+ m_var_filterp_r_4 = 0;
+ m_var_filterp_r_5 = 0;
+ m_var_filterp_r_6 = 0;
+
+ m_var_h1 = 0;
+ m_var_h2 = 0;
+ m_var_h3 = 0;
+ m_var_h4 = 0;
+
+ m_cho_lfo_phase = 0;
+ m_cho_filter_l_1 = 0;
+ m_cho_filter_l_2 = 0;
+ m_cho_filter_l_3 = 0;
+ m_cho_filter_r_1 = 0;
+ m_cho_filter_r_2 = 0;
+ m_cho_filter_r_3 = 0;
+
+ m_rev_filter_1 = 0;
+ m_rev_filter_2 = 0;
+ m_rev_filter_3 = 0;
+ m_rev_hist_a = 0;
+ m_rev_hist_b = 0;
+ m_rev_hist_c = 0;
+ m_rev_hist_d = 0;
+
+ std::fill(m_rev_buffer.begin(), m_rev_buffer.end(), 0);
+ std::fill(m_cho_buffer.begin(), m_cho_buffer.end(), 0);
+ std::fill(m_var_buffer.begin(), m_var_buffer.end(), 0);
+ std::fill(m_offset.begin(), m_offset.end(), 0);
+ std::fill(m_const.begin(), m_const.end(), 0);
+ std::fill(m_lpf_info.begin(), m_lpf_info.end(), 0);
+ std::fill(m_lpf_speed.begin(), m_lpf_speed.end(), 0);
+ std::fill(m_lfo_famod_depth.begin(), m_lfo_famod_depth.end(), 0);
+ std::fill(m_rev_level.begin(), m_rev_level.end(), 0);
+ std::fill(m_dry_level.begin(), m_dry_level.end(), 0);
+ std::fill(m_cho_level.begin(), m_cho_level.end(), 0);
+ std::fill(m_var_level.begin(), m_var_level.end(), 0);
+ std::fill(m_glo_level.begin(), m_glo_level.end(), 0);
+ std::fill(m_panning.begin(), m_panning.end(), 0);
+ std::fill(m_attack_speed.begin(), m_attack_speed.end(), 0);
+ std::fill(m_attack_level.begin(), m_attack_level.end(), 0);
+ std::fill(m_decay_speed.begin(), m_decay_speed.end(), 0);
+ std::fill(m_decay_level.begin(), m_decay_level.end(), 0);
+ std::fill(m_pitch.begin(), m_pitch.end(), 0);
+ std::fill(m_sample_start.begin(), m_sample_start.end(), 0);
+ std::fill(m_sample_end.begin(), m_sample_end.end(), 0);
+ std::fill(m_sample_dec_and_format.begin(), m_sample_dec_and_format.end(), 0);
+ std::fill(m_sample_address.begin(), m_sample_address.end(), 0);
+ std::fill(m_lfo_step.begin(), m_lfo_step.end(), 0);
+ std::fill(m_lfo_pmod_depth.begin(), m_lfo_pmod_depth.end(), 0);
+
+ std::fill(m_lfo_phase.begin(), m_lfo_phase.end(), 0);
+ std::fill(m_sample_pos.begin(), m_sample_pos.end(), 0);
+ std::fill(m_envelope_level.begin(), m_envelope_level.end(), 0);
+
+ std::fill(m_glo_level_cur.begin(), m_glo_level_cur.end(), 0);
+ std::fill(m_pan_l.begin(), m_pan_l.end(), 0);
+ std::fill(m_pan_r.begin(), m_pan_r.end(), 0);
+
+ std::fill(m_lpf_feedback.begin(), m_lpf_feedback.end(), 0);
+ std::fill(m_lpf_target_value.begin(), m_lpf_target_value.end(), 0);
+ std::fill(m_lpf_value.begin(), m_lpf_value.end(), 0);
+ std::fill(m_lpf_timer.begin(), m_lpf_timer.end(), 0);
+ std::fill(m_lpf_ha.begin(), m_lpf_ha.end(), 0);
+ std::fill(m_lpf_hb.begin(), m_lpf_hb.end(), 0);
+
+ std::fill(m_active.begin(), m_active.end(), false);
+ std::fill(m_decay.begin(), m_decay.end(), false);
+ std::fill(m_decay_done.begin(), m_decay_done.end(), false);
+ std::fill(m_lpf_done.begin(), m_lpf_done.end(), false);
+
+ std::fill(m_dpcm_current.begin(), m_dpcm_current.end(), false);
+ std::fill(m_dpcm_next.begin(), m_dpcm_next.end(), false);
+ std::fill(m_dpcm_address.begin(), m_dpcm_address.end(), false);
}
-void swp00_device::rom_bank_updated()
+void swp00_device::rom_bank_pre_change()
{
m_stream->update();
}
void swp00_device::map(address_map &map)
{
- map(0x0000, 0x7ff).rw(FUNC(swp00_device::snd_r), FUNC(swp00_device::snd_w));
+ map(0x000, 0x7ff).rw(FUNC(swp00_device::snd_r), FUNC(swp00_device::snd_w));
+
+ // 00-01: control
+
+ rchan(map, 0x08).w(FUNC(swp00_device::slot8_w)); // always 80
+ rchan(map, 0x09).w(FUNC(swp00_device::slot9_w)); // always 00
+ rchan(map, 0x0a).rw(FUNC(swp00_device::sample_start_r<1>), FUNC(swp00_device::sample_start_w<1>));
+ rchan(map, 0x0b).rw(FUNC(swp00_device::sample_start_r<0>), FUNC(swp00_device::sample_start_w<0>));
+
+ // 0c-0f: meg offsets
+ // 10-1b: meg values
+
+ rchan(map, 0x20).rw(FUNC(swp00_device::lpf_info_r<1>), FUNC(swp00_device::lpf_info_w<1>));
+ rchan(map, 0x21).rw(FUNC(swp00_device::lpf_info_r<0>), FUNC(swp00_device::lpf_info_w<0>));
+ rchan(map, 0x22).rw(FUNC(swp00_device::lpf_speed_r), FUNC(swp00_device::lpf_speed_w));
+ rchan(map, 0x23).rw(FUNC(swp00_device::lfo_famod_depth_r), FUNC(swp00_device::lfo_famod_depth_w));
+ rchan(map, 0x24).rw(FUNC(swp00_device::lfo_step_r), FUNC(swp00_device::lfo_step_w));
+ rchan(map, 0x25).rw(FUNC(swp00_device::lfo_pmod_depth_r), FUNC(swp00_device::lfo_pmod_depth_w));
+ rchan(map, 0x26).rw(FUNC(swp00_device::attack_speed_r), FUNC(swp00_device::attack_speed_w));
+ rchan(map, 0x27).rw(FUNC(swp00_device::attack_level_r), FUNC(swp00_device::attack_level_w));
+ rchan(map, 0x28).rw(FUNC(swp00_device::decay_speed_r), FUNC(swp00_device::decay_speed_w));
+ rchan(map, 0x29).rw(FUNC(swp00_device::decay_level_r), FUNC(swp00_device::decay_level_w));
+ rchan(map, 0x2a).rw(FUNC(swp00_device::rev_level_r), FUNC(swp00_device::rev_level_w));
+ rchan(map, 0x2b).rw(FUNC(swp00_device::dry_level_r), FUNC(swp00_device::dry_level_w));
+ rchan(map, 0x2c).rw(FUNC(swp00_device::cho_level_r), FUNC(swp00_device::cho_level_w));
+ rchan(map, 0x2d).rw(FUNC(swp00_device::var_level_r), FUNC(swp00_device::var_level_w));
+ rchan(map, 0x2e).rw(FUNC(swp00_device::glo_level_r), FUNC(swp00_device::glo_level_w));
+ rchan(map, 0x2f).rw(FUNC(swp00_device::panning_r), FUNC(swp00_device::panning_w));
+ rchan(map, 0x30).rw(FUNC(swp00_device::sample_dec_and_format_r), FUNC(swp00_device::sample_dec_and_format_w));
+ rchan(map, 0x31).rw(FUNC(swp00_device::sample_address_r<2>), FUNC(swp00_device::sample_address_w<2>));
+ rchan(map, 0x32).rw(FUNC(swp00_device::sample_address_r<1>), FUNC(swp00_device::sample_address_w<1>));
+ rchan(map, 0x33).rw(FUNC(swp00_device::sample_address_r<0>), FUNC(swp00_device::sample_address_w<0>));
+ rchan(map, 0x34).rw(FUNC(swp00_device::pitch_r<1>), FUNC(swp00_device::pitch_w<1>));
+ rchan(map, 0x35).rw(FUNC(swp00_device::pitch_r<0>), FUNC(swp00_device::pitch_w<0>));
+ rchan(map, 0x36).rw(FUNC(swp00_device::sample_end_r<1>), FUNC(swp00_device::sample_end_w<1>));
+ rchan(map, 0x37).rw(FUNC(swp00_device::sample_end_r<0>), FUNC(swp00_device::sample_end_w<0>));
+
+ rctrl(map, 0x00); // 01 at startup
+ rctrl(map, 0x01).rw(FUNC(swp00_device::state_r), FUNC(swp00_device::state_adr_w));
+ rctrl(map, 0x02).rw(FUNC(swp00_device::waverom_access_r), FUNC(swp00_device::waverom_access_w));
+ rctrl(map, 0x03).r(FUNC(swp00_device::waverom_val_r));
+ rctrl(map, 0x04).rw(FUNC(swp00_device::meg_control_r), FUNC(swp00_device::meg_control_w));
+ rctrl(map, 0x08).w(FUNC(swp00_device::keyon_w<3>));
+ rctrl(map, 0x09).w(FUNC(swp00_device::keyon_w<2>));
+ rctrl(map, 0x0a).w(FUNC(swp00_device::keyon_w<1>));
+ rctrl(map, 0x0b).w(FUNC(swp00_device::keyon_w<0>));
+ rctrl(map, 0x0c); // 00 at startup
+ rctrl(map, 0x0d); // 00 at startup
+ rctrl(map, 0x0e); // 00 at startup
+
+ map(0x180, 0x1ff).rw(FUNC(swp00_device::offset_r), FUNC(swp00_device::offset_w));
+ map(0x200, 0x37f).rw(FUNC(swp00_device::const_r), FUNC(swp00_device::const_w));
}
-static u8 rr[0x20*0x20];
+// Voice control
-u8 swp00_device::snd_r(offs_t offset)
+void swp00_device::slot8_w(offs_t offset, u8 data)
{
- if(0) {
- int chan = (offset >> 6) & 0x3f;
- int slot = offset & 0x3f;
- std::string preg = "-";
- if(slot >= 0x21 && slot <= 0x2b && (slot & 1))
- preg = util::string_format("fp%03x", (slot-0x21)/2 + 6*chan);
- else if(slot == 0x30 || slot == 0x31)
- preg = util::string_format("dt%02x", (slot-0x30) + 2*chan);
- else if(slot == 0x0e || slot == 0x0f)
- preg = util::string_format("ct%02x", (slot-0x0e) + 2*chan);
- else
- preg = util::string_format("%02x.%02x", chan, slot);
- logerror("snd_r [%04x %04x] %-5s, %04x\n", offset, offset*2, preg, rr[offset]);
- }
- if(0 && offset == 0x080f)
- machine().debug_break();
- if(offset == 0x080f)
- return 0;
- return rr[offset];
+ if(data == 0x80)
+ return;
+ logerror("slot8[%02x] = %02x\n", offset >> 1, data);
}
-void swp00_device::snd_w(offs_t offset, u8 data)
+void swp00_device::slot9_w(offs_t offset, u8 data)
+{
+ if(data == 0x00)
+ return;
+ logerror("slot9[%02x] = %02x\n", offset >> 1, data);
+}
+
+template<int sel> void swp00_device::lpf_info_w(offs_t offset, u8 data)
{
- if(rr[offset] == data)
+ int chan = offset >> 1;
+ u16 old = m_lpf_info[chan];
+ m_stream->update();
+
+ m_lpf_info[chan] = (m_lpf_info[chan] & ~(0xff << (8*sel))) | (data << (8*sel));
+ if(m_lpf_info[chan] == old)
+ return;
+
+ // if(!sel)
+ // logerror("lpf_info[%02x] = %04x\n", chan, m_lpf_info[chan]);
+
+ u32 fb = m_lpf_info[chan] >> 11;
+ u32 level = m_lpf_info[chan] & 0x7ff;
+ if(fb < 4 && level > 0x7c0)
+ level = 0x7c0;
+ if(level)
+ level |= 0x800;
+ m_lpf_feedback[chan] = (fb + 4) << 21;
+ m_lpf_target_value[chan] = level << 14;
+}
+
+template<int sel> u8 swp00_device::lpf_info_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_lpf_info[chan] >> (8*sel);
+}
+
+void swp00_device::lpf_speed_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_lpf_speed[chan] == data)
+ return;
+ m_stream->update();
+ m_lpf_speed[chan] = data;
+ // logerror("lpf_speed[%02x] = %02x\n", chan, m_lpf_speed[chan]);
+}
+
+u8 swp00_device::lpf_speed_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_lpf_speed[chan];
+}
+
+void swp00_device::lfo_famod_depth_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_lfo_famod_depth[chan] == data)
return;
+ m_stream->update();
+ m_lfo_famod_depth[chan] = data;
+ // logerror("lfo_famod_depth[%02x] = %02x\n", chan, m_lfo_famod_depth[chan]);
+}
+
+u8 swp00_device::lfo_famod_depth_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_lfo_famod_depth[chan];
+}
+
+void swp00_device::rev_level_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_rev_level[chan] == data)
+ return;
+ m_stream->update();
+ m_rev_level[chan] = data;
+ // logerror("rev_level[%02x] = %02x\n", chan, m_rev_level[chan]);
+}
+
+u8 swp00_device::rev_level_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_rev_level[chan];
+}
+
+void swp00_device::dry_level_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_dry_level[chan] == data)
+ return;
+ m_stream->update();
+ m_dry_level[chan] = data;
+ // logerror("dry_level[%02x] = %02x\n", chan, m_dry_level[chan]);
+}
+
+u8 swp00_device::dry_level_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_dry_level[chan];
+}
+
+void swp00_device::cho_level_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_cho_level[chan] == data)
+ return;
+ m_stream->update();
+ m_cho_level[chan] = data;
+ // logerror("cho_level[%02x] = %02x\n", chan, m_cho_level[chan]);
+}
+
+u8 swp00_device::cho_level_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_cho_level[chan];
+}
+
+void swp00_device::var_level_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_var_level[chan] == data)
+ return;
+ m_stream->update();
+ m_var_level[chan] = data;
+ // logerror("var_level[%02x] = %02x\n", chan, m_var_level[chan]);
+}
+
+u8 swp00_device::var_level_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_var_level[chan];
+}
+
+void swp00_device::glo_level_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_glo_level[chan] == data)
+ return;
+ m_glo_level[chan] = data;
+ // logerror("glo_level[%02x] = %02x\n", chan, m_glo_level[chan]);
+}
+
+u8 swp00_device::glo_level_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_glo_level[chan];
+}
+
+void swp00_device::panning_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_panning[chan] == data)
+ return;
+ m_stream->update();
+ m_panning[chan] = data;
+ // logerror("panning[%02x] = %02x\n", chan, m_panning[chan]);
+}
+
+u8 swp00_device::panning_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_panning[chan];
+}
+
+void swp00_device::attack_speed_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_attack_speed[chan] == data)
+ return;
+ m_stream->update();
+ m_attack_speed[chan] = data;
+ logerror("attack_speed[%02x] = %02x\n", chan, m_attack_speed[chan]);
+}
+
+u8 swp00_device::attack_speed_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_attack_speed[chan];
+}
+
+void swp00_device::attack_level_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_attack_level[chan] == data)
+ return;
+ m_stream->update();
+ m_attack_level[chan] = data;
+ logerror("attack_level[%02x] = %02x\n", chan, m_attack_level[chan]);
+}
+
+u8 swp00_device::attack_level_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_attack_level[chan];
+}
+
+void swp00_device::decay_speed_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_decay_speed[chan] == data)
+ return;
+
+ m_stream->update();
+ m_decay_speed[chan] = data;
+
+ if(data & 0x80)
+ m_decay[chan] = true;
+
+ logerror("decay_speed[%02x] = %02x\n", chan, m_decay_speed[chan]);
+}
+
+u8 swp00_device::decay_speed_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_decay_speed[chan];
+}
+
+void swp00_device::decay_level_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_decay_level[chan] == data)
+ return;
+ m_stream->update();
+ m_decay_level[chan] = data;
+ logerror("decay_level[%02x] = %02x\n", chan, m_decay_level[chan]);
+}
+
+u8 swp00_device::decay_level_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_decay_level[chan];
+}
+
+template<int sel> void swp00_device::pitch_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ u16 old = m_pitch[chan];
+ m_stream->update();
+ m_pitch[chan] = (m_pitch[chan] & ~(0xff << (8*sel))) | (data << (8*sel));
+ if(m_pitch[chan] == old)
+ return;
+ // if(!sel)
+ // logerror("pitch[%02x] = %04x\n", chan, m_pitch[chan]);
+}
+
+template<int sel> u8 swp00_device::pitch_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_pitch[chan] >> (8*sel);
+}
+
+template<int sel> void swp00_device::sample_start_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ m_stream->update();
+
+ m_sample_start[chan] = (m_sample_start[chan] & ~(0xff << (8*sel))) | (data << (8*sel));
+ // if(!sel)
+ // logerror("sample_start[%02x] = %04x\n", chan, m_sample_start[chan]);
+}
+
+template<int sel> u8 swp00_device::sample_start_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_sample_start[chan] >> (8*sel);
+}
+
+template<int sel> void swp00_device::sample_end_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ m_stream->update();
+
+ m_sample_end[chan] = (m_sample_end[chan] & ~(0xff << (8*sel))) | (data << (8*sel));
+ // if(!sel)
+ // logerror("sample_end[%02x] = %04x\n", chan, m_sample_end[chan]);
+}
+
+template<int sel> u8 swp00_device::sample_end_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_sample_end[chan] >> (8*sel);
+}
+
+void swp00_device::sample_dec_and_format_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ m_stream->update();
+
+ m_sample_dec_and_format[chan] = data;
+ // logerror("sample_dec_and_format[%02x] = %02x\n", chan, m_sample_dec_and_format[chan]);
+}
+
+u8 swp00_device::sample_dec_and_format_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_sample_dec_and_format[chan];
+}
+
+template<int sel> void swp00_device::sample_address_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ m_stream->update();
+
+ m_sample_address[chan] = (m_sample_address[chan] & ~(0xff << (8*sel))) | (data << (8*sel));
+ // if(!sel)
+ // logerror("sample_address[%02x] = %04x\n", chan, m_sample_address[chan]);
+}
+
+template<int sel> u8 swp00_device::sample_address_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_sample_address[chan] >> (8*sel);
+}
+
+void swp00_device::lfo_step_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_lfo_step[chan] == data)
+ return;
+ m_stream->update();
+
+ m_lfo_step[chan] = data;
+ // logerror("lfo_step[%02x] = %02x\n", chan, m_lfo_step[chan]);
+}
+
+u8 swp00_device::lfo_step_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_lfo_step[chan];
+}
+
+void swp00_device::lfo_pmod_depth_w(offs_t offset, u8 data)
+{
+ int chan = offset >> 1;
+ if(m_lfo_pmod_depth[chan] == data)
+ return;
+ m_stream->update();
+
+ m_lfo_pmod_depth[chan] = data;
+ // logerror("lfo_pmod_depth[%02x] = %02x\n", chan, m_lfo_pmod_depth[chan]);
+}
+
+u8 swp00_device::lfo_pmod_depth_r(offs_t offset)
+{
+ int chan = offset >> 1;
+ return m_lfo_pmod_depth[chan];
+}
+
+void swp00_device::keyon(int chan)
+{
+ m_stream->update();
+ logerror("keyon %02x a=%02x/%02x d=%02x/%02x glo=%02x pan=%02x [%x %x %x %x]\n", chan, m_attack_speed[chan], m_attack_level[chan], m_decay_speed[chan], m_decay_level[chan], m_glo_level[chan], m_panning[chan], m_sample_start[chan], m_sample_end[chan], m_sample_address[chan], m_sample_dec_and_format[chan]);
+ m_lfo_phase[chan] = 0;
+ m_sample_pos[chan] = -m_sample_start[chan] << 15;
+
+ m_sample_pos[chan] = 0;
+
+ m_active[chan] = true;
+ m_decay[chan] = false;
+ m_decay_done[chan] = false;
- rr[offset] = data;
+ m_dpcm_current[chan] = 0;
+ m_dpcm_next[chan] = 0;
+ m_dpcm_address[chan] = m_sample_address[chan] - m_sample_start[chan];
- int chan = (offset >> 6) & 0x1f;
- int slot = offset & 0x3f;
+ m_lpf_value[chan] = m_lpf_target_value[chan];
+ m_lpf_timer[chan] = 0x4000000;
+ m_lpf_ha[chan] = 0;
+ m_lpf_hb[chan] = 0;
- std::string preg = "-";
-#if 0
- if(slot >= 0x21 && slot <= 0x2b && (slot & 1))
- preg = util::string_format("fp%03x", (slot-0x21)/2 + 6*chan);
- else if(slot == 0x0e || slot == 0x0f)
- preg = util::string_format("sy%02x", (slot-0x0e) + 2*chan);
- else if(slot == 0x30 || slot == 0x31)
- preg = util::string_format("dt%02x", (slot-0x30) + 2*chan);
- else if(slot == 0x38)
- preg = util::string_format("vl%02x", chan);
- else if(slot == 0x3e || slot == 0x3f)
- preg = util::string_format("lf%02x", (slot-0x3e) + 2*chan);
+ m_glo_level_cur[chan] = m_glo_level[chan] << 4;
+ m_pan_l[chan] = panmap[m_panning[chan] >> 4];
+ m_pan_r[chan] = panmap[m_panning[chan] & 15];
+
+ if(m_decay_speed[chan] & 0x80) {
+ m_envelope_level[chan] = 0;
+ m_decay[chan] = true;
+ } else if((m_attack_speed[chan] & 0x80) || m_attack_level[chan])
+ m_envelope_level[chan] = m_attack_level[chan] << 20;
+ else
+ m_envelope_level[chan] = 0x8000000;
+}
+
+template<int sel> void swp00_device::keyon_w(u8 data)
+{
+ for(int i=0; i < 8; i++)
+ if(BIT(data, i))
+ keyon(8*sel+i);
+}
+
+void swp00_device::offset_w(offs_t offset, u8 data)
+{
+ m_stream->update();
+
+ if(offset & 1)
+ m_offset[offset >> 1] = (m_offset[offset >> 1] & 0xff00) | data;
+ else
+ m_offset[offset >> 1] = (m_offset[offset >> 1] & 0x00ff) | (data << 8);
+ if(0)
+ if(offset & 1)
+ logerror("offset[%02x] = %04x\n", 3*(offset >> 1), m_offset[offset >> 1]);
+}
+
+u8 swp00_device::offset_r(offs_t offset)
+{
+ if(offset & 1)
+ return m_offset[offset >> 1];
+ else
+ return m_offset[offset >> 1] >> 8;
+}
+
+void swp00_device::const_w(offs_t offset, u8 data)
+{
+ m_stream->update();
+
+ if(offset & 1)
+ m_const[offset >> 1] = (m_const[offset >> 1] & 0xff00) | data;
else
-#endif
- preg = util::string_format("%02x.%02x", chan, slot);
+ m_const[offset >> 1] = (m_const[offset >> 1] & 0x00ff) | (data << 8);
+ if(0)
+ if(offset & 1)
+ logerror("const[%02x] = %04x\n", offset >> 1, m_const[offset >> 1]);
+}
+
+u8 swp00_device::const_r(offs_t offset)
+{
+ if(offset & 1)
+ return m_const[offset >> 1];
+ else
+ return m_const[offset >> 1] >> 8;
+}
+
+void swp00_device::waverom_access_w(u8 data)
+{
+ m_waverom_access = data;
+}
+
+u8 swp00_device::waverom_access_r()
+{
+ return 0x00; // non-zero = busy reading the rom
+}
+
+u8 swp00_device::waverom_val_r()
+{
+ u8 val = read_byte(m_sample_address[0x1f]);
+ logerror("waverom read adr=%08x -> %02x\n", m_sample_address[0x1f], val);
+ m_sample_address[0x1f] = (m_sample_address[0x1f] + 1) & 0xffffff;
+ return val;
+}
- logerror("snd_w [%04x %04x] %-5s, %02x\n", offset, offset*2, preg, data);
+void swp00_device::meg_control_w(u8 data)
+{
+ m_meg_control = data;
+ logerror("meg_control %02x (variation %x, %s)\n", m_meg_control, m_meg_control >> 6, m_meg_control & 2 ? "mute" : "on");
+}
+
+u8 swp00_device::meg_control_r()
+{
+ return m_meg_control;
+}
+
+// Counters state access
+u8 swp00_device::state_r()
+{
+ m_stream->update();
+
+ int chan = m_state_adr & 0x1f;
+ switch(m_state_adr & 0xe0) {
+ case 0x00: // lpf value
+ return (m_lpf_value[chan] >> 20) | (m_lpf_done[chan] ? 0x80 : 0x00);
+
+ case 0x40: { // Envelope state
+ if(!m_active[chan])
+ return 0xff;
+
+ u8 vol;
+ if(m_decay[chan] || m_attack_level[chan] || (m_attack_speed[chan] & 0x80))
+ vol = m_envelope_level[chan] >> 22;
+ else
+ vol = 0;
+
+ if(m_decay_done[chan])
+ vol |= 0x40;
+ if(m_decay[chan])
+ vol |= 0x80;
+
+ return vol;
+ }
+
+ case 0x60: // global level
+ return (m_glo_level_cur[chan] >> 6) | ((m_glo_level_cur[chan] == (m_glo_level[chan] << 4)) ? 0x80 : 0x00);
+
+ case 0x80: // panning l
+ return (m_pan_l[chan] >> 6) | ((m_pan_l[chan] == panmap[m_panning[chan] >> 4]) ? 0x80 : 0x00);
+
+ case 0xa0: // panning r
+ return (m_pan_r[chan] >> 6) | ((m_pan_r[chan] == panmap[m_panning[chan] & 15]) ? 0x80 : 0x00);
+ }
+
+ logerror("state %02x unsupported\n");
+ return 0;
+}
+
+void swp00_device::state_adr_w(u8 data)
+{
+ m_state_adr = data;
+}
+
+
+// Catch-all
+
+u8 swp00_device::snd_r(offs_t offset)
+{
+ logerror("snd_r [%03x]\n", offset);
+ return 0;
+}
+
+void swp00_device::snd_w(offs_t offset, u8 data)
+{
+ logerror("snd_w [%03x] %02x\n", offset, data);
}
// Synthesis
+s32 swp00_device::rext(int reg) const
+{
+ s32 val = m_const[reg] & 0x3ff;
+ if(val > 0x200) // Not 100% a real 2-complement fixed-point, e.g. the max value is positive, not negative
+ val |= 0xfffffc00;
+ return val;
+}
+
+s32 swp00_device::m7v(s32 value, s32 mult)
+{
+ return (s64(value) * mult) >> 7;
+}
+
+s32 swp00_device::m7(s32 value, int reg) const
+{
+ return m7v(value, rext(reg));
+}
+
+s32 swp00_device::m9v(s32 value, s32 mult)
+{
+ return (s64(value) * mult) >> 9;
+}
+
+s32 swp00_device::m9(s32 value, int reg) const
+{
+ return m9v(value, rext(reg));
+}
+
+template<size_t size> swp00_device::delay_block<size>::delay_block(swp00_device *swp, std::array<s32, size> &buffer) :
+ m_swp(swp),
+ m_buffer(buffer)
+{
+}
+
+template<size_t size> s32 swp00_device::delay_block<size>::r(int offreg) const
+{
+ return m_buffer[(m_swp->m_buffer_offset + m_swp->m_offset[offreg/3]) & (size - 1)];
+}
+
+template<size_t size> void swp00_device::delay_block<size>::w(int offreg, s32 value) const
+{
+ m_buffer[(m_swp->m_buffer_offset + m_swp->m_offset[offreg/3]) & (size - 1)] = value;
+}
+
+template<size_t size> s32 swp00_device::delay_block<size>::rlfo(int offreg, u32 phase, s32 delta_phase, int levelreg) const
+{
+ // Phase is on 23 bits
+ // Delta phase is on 10 bits shifts for a maximum of a full period (e.g. left shift of 13)
+ // Phase is wrapped into a triangle on 22 bits
+ // Level register is 10 bits where 1 = 4 samples of offset, for a maximum of 4096 samples
+
+ u32 lfo_phase = lfo_wrap(phase, delta_phase);
+
+ // Offset is 12.22
+ u64 lfo_offset = lfo_phase * m_swp->rext(levelreg);
+ u32 lfo_i_offset = lfo_offset >> 22;
+ s32 lfo_i_frac = lfo_offset & 0x3fffff;
+
+ // Uses in reality offreg and offreg+3 (which are offset by 1)
+ u32 pos = m_swp->m_buffer_offset + m_swp->m_offset[offreg/3] + lfo_i_offset;
+ s32 val0 = m_buffer[pos & (size - 1)];
+ s32 val1 = m_buffer[(pos + 1) & (size - 1)];
+
+ // fprintf(stderr, "lfo %02x %x %x\n", offreg, val0, val1);
+ return s32((val1 * s64(lfo_i_frac) + val0 * s64(0x400000 - lfo_i_frac)) >> 22);
+}
+
+template<size_t size> s32 swp00_device::delay_block<size>::rlfo2(int offreg, s32 offset) const
+{
+ // Offset is 12.11
+ u32 lfo_i_offset = offset >> 11;
+ s32 lfo_i_frac = offset & 0x7ff;
+
+ // Uses in reality offreg and offreg+3 (which are offset by 1)
+ u32 pos = m_swp->m_buffer_offset + m_swp->m_offset[offreg/3] + lfo_i_offset;
+ s32 val0 = m_buffer[pos & (size - 1)];
+ s32 val1 = m_buffer[(pos + 1) & (size - 1)];
+
+ // fprintf(stderr, "lfo %02x %x %x\n", offreg, val0, val1);
+ return s32((val1 * s64(lfo_i_frac) + val0 * s64(0x800 - lfo_i_frac)) >> 11);
+}
+
+s32 swp00_device::lfo_get_step(int reg) const
+{
+ u32 e = (m_const[reg] >> 7) & 7;
+ return (m_const[reg] & 0x7f) << (e == 7 ? 15 : e);
+}
+
+void swp00_device::lfo_step(u32 &phase, int reg) const
+{
+ phase = (phase + lfo_get_step(reg)) & 0x7fffff;
+}
+
+s32 swp00_device::lfo_saturate(s32 phase)
+{
+ if(phase < -0x400000)
+ return -0x400000;
+ if(phase >= 0x400000)
+ return 0x3fffff;
+ return phase;
+}
+
+u32 swp00_device::lfo_wrap(s32 phase, s32 delta_phase)
+{
+ s32 lfo_phase = (phase - (delta_phase << 13)) & 0x7fffff;
+ if(lfo_phase & 0x400000)
+ lfo_phase ^= 0x7fffff;
+ return lfo_phase;
+}
+
+void swp00_device::filtered_lfo_step(s32 &position, s32 phase, int deltareg, int postdeltareg, int scalereg, int feedbackreg)
+{
+ s32 phase1 = lfo_saturate((deltareg == -1 ? phase : lfo_wrap(phase, deltareg)) - (rext(postdeltareg) << 13));
+ s64 phase2 = s64(lfo_get_step(scalereg)) * phase1 + s64(0x400000 - lfo_get_step(feedbackreg)) * position;
+ position = phase2 >> 22;
+}
+
+s32 swp00_device::alfo(u32 phase, s32 delta_phase, int levelreg, int offsetreg, bool sub) const
+{
+ u32 lfo_phase = lfo_wrap(phase, delta_phase);
+ s32 offset = rext(offsetreg);
+ if(sub)
+ offset = -offset;
+ s32 base = s32((s64(lfo_phase) * rext(levelreg)) >> 19) + (offset << 3);
+ s32 bamp = ((base & 0x1ff) | 0x200) << ((base >> 9) & 15);
+ bamp >>= 8;
+ if(bamp <= -0x200)
+ bamp = -0x1ff;
+ else if(bamp >= 0x200)
+ bamp = 0x200;
+ return bamp;
+}
+
+s32 swp00_device::lfo_mod(s32 phase, int scalereg) const
+{
+ return (m9(phase, scalereg) >> 13) + 0x200;
+}
+
+s32 swp00_device::lfo_scale(s32 phase, int scalereg) const
+{
+ return lfo_saturate((phase - (rext(scalereg) << 13)) * 4);
+}
+
+s32 swp00_device::lfo_wrap_reg(s32 phase, int deltareg) const
+{
+ return lfo_wrap(phase, rext(deltareg));
+}
+
+s32 swp00_device::sx(int reg) const
+{
+ s32 mult = m_const[reg];
+ if(mult & 0x200)
+ mult |= 0xfffffc00;
+ return mult;
+}
+
+double swp00_device::sx7(int reg) const
+{
+ return sx(reg) / 128.0;
+}
+
+double swp00_device::sx9(int reg) const
+{
+ return sx(reg) / 512.0;
+}
+
+s32 swp00_device::saturate(s32 value)
+{
+ if(value <= -0x20000)
+ return -0x20000;
+ else if(value > 0x1ffff)
+ return 0x1ffff;
+ else
+ return value;
+}
+
+double v2f2(s32 value)
+{
+ return (1.0 - (value & 0xffffff) / 33554432.0) / (1 << (value >> 24));
+}
+
void swp00_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
{
- outputs[0].fill(0);
- outputs[1].fill(0);
+ const delay_block brev(this, m_rev_buffer);
+ const delay_block bcho(this, m_cho_buffer);
+ const delay_block bvar(this, m_var_buffer);
+
+ for(int i=0; i != outputs[0].samples(); i++) {
+ s32 dry_l = 0, dry_r = 0;
+ s32 rev = 0;
+ s32 cho_l = 0, cho_r = 0;
+ s32 var_l = 0, var_r = 0;
+
+ for(int chan = 0; chan != 32; chan++) {
+ if(!m_active[chan])
+ continue;
+
+ u32 lfo_phase = m_lfo_phase[chan] >> 7;
+ s32 lfo_p_phase = lfo_phase ^ (m_lfo_step[chan] & 0x40 ? lfo_shape_centered_tri : lfo_shape_centered_saw)[lfo_phase >> 18];
+ s32 lfo_fa_phase = lfo_phase ^ (m_lfo_step[chan] & 0x40 ? lfo_shape_offset_tri : lfo_shape_offset_saw )[lfo_phase >> 18];
+
+ s16 val0, val1;
+ u32 base_address = m_sample_address[chan];
+ s32 spos = m_sample_pos[chan] >> 15;
+ switch(m_sample_dec_and_format[chan] >> 6) {
+ case 0: { // 16-bits linear
+ offs_t adr = base_address + (spos << 1);
+ val0 = read_word(adr);
+ val1 = read_word(adr+2);
+ break;
+ }
+
+ case 1: { // 12-bits linear
+ offs_t adr = base_address + (spos >> 2)*6;
+ switch(spos & 3) {
+ case 0: { // Cabc ..AB .... ....
+ u16 w0 = read_word(adr);
+ u16 w1 = read_word(adr+2);
+ val0 = (w0 & 0x0fff) << 4;
+ val1 = ((w0 & 0xf000) >> 8) | ((w1 & 0x00ff) << 8);
+ break;
+ }
+ case 1: { // c... BCab ...A ....
+ u16 w0 = read_word(adr);
+ u16 w1 = read_word(adr+2);
+ u16 w2 = read_word(adr+4);
+ val0 = ((w0 & 0xf000) >> 8) | ((w1 & 0x00ff) << 8);
+ val1 = ((w1 & 0xff00) >> 4) | ((w2 & 0x000f) << 12);
+ break;
+ }
+ case 2: { // .... bc.. ABCa ....
+ u16 w1 = read_word(adr+2);
+ u16 w2 = read_word(adr+4);
+ val0 = ((w1 & 0xff00) >> 4) | ((w2 & 0x000f) << 12);
+ val1 = w2 & 0xfff0;
+ break;
+ }
+ case 3: { // .... .... abc. .ABC
+ u16 w2 = read_word(adr+4);
+ u16 w3 = read_word(adr+6);
+ val0 = w2 & 0xfff0;
+ val1 = (w3 & 0x0fff) << 4;
+ break;
+ }
+ }
+ break;
+ }
+
+ case 2: // 8-bits linear
+ val0 = (read_byte(base_address + spos) << 8);
+ val1 = (read_byte(base_address + spos + 1) << 8);
+ logerror("XX %04x %04x\n", val0, val1);
+ break;
+
+ case 3: { // 8-bits delta-pcm
+ u32 target_address = base_address + spos + 1;
+ while(m_dpcm_address[chan] <= target_address) {
+ m_dpcm_current[chan] = m_dpcm_next[chan];
+ s32 sample = m_dpcm_next[chan] + m_dpcm[read_byte(m_dpcm_address[chan])];
+ m_dpcm_address[chan] ++;
+ if(sample < -0x8000)
+ sample = -0x8000;
+ else if(sample > 0x7fff)
+ sample = 0x7fff;
+ m_dpcm_next[chan] = sample;
+ }
+ val0 = m_dpcm_current[chan];
+ val1 = m_dpcm_next[chan];
+ break;
+ }
+ }
+
+ s32 mul = m_sample_pos[chan] & 0x7fff;
+ s32 sample = (val1 * mul + val0 * (0x8000 - mul)) >> 7;
+
+ s32 lpf_value = m_lpf_value[chan] + ((lfo_fa_phase * (m_lfo_famod_depth[chan] >> 5)) << (m_lfo_step[chan] & 0x40 ? 2 : 1));
+
+ m_lpf_ha[chan] += lpffpapply(lpf_value, sample - 2*fpapply(m_lpf_feedback[chan], m_lpf_ha[chan]) - m_lpf_hb[chan]);
+ m_lpf_hb[chan] += lpffpapply(lpf_value, m_lpf_ha[chan]);
+
+ sample = m_lpf_hb[chan];
+
+ s32 envelope_level;
+ if(m_decay[chan] || m_attack_level[chan] || (m_attack_speed[chan] & 0x80))
+ envelope_level = m_envelope_level[chan];
+ else
+ envelope_level = 0;
+
+ s32 tremolo_level = (lfo_fa_phase * (m_lfo_famod_depth[chan] & 0x1f)) << ((m_lfo_step[chan] & 0x40) ? 3 : 2);
+
+ dry_l += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + (m_dry_level[chan] << 20) + (m_pan_l[chan] << 16), sample);
+ dry_r += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + (m_dry_level[chan] << 20) + (m_pan_r[chan] << 16), sample);
+ rev += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + (m_rev_level[chan] << 20), sample);
+ cho_l += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + (m_cho_level[chan] << 20) + (m_pan_l[chan] << 16), sample);
+ cho_r += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + (m_cho_level[chan] << 20) + (m_pan_r[chan] << 16), sample);
+ var_l += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + (m_var_level[chan] << 20) + (m_pan_l[chan] << 16), sample);
+ var_r += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + (m_var_level[chan] << 20) + (m_pan_r[chan] << 16), sample);
+
+ m_lfo_phase[chan] = (m_lfo_phase[chan] + m_global_step[0x20 + (m_lfo_step[chan] & 0x3f)]) & 0x7ffffff;
+
+ u32 sample_increment = ((m_pitch[chan] & 0xfff) << (8 + (s16(m_pitch[chan]) >> 12))) >> 4;
+ m_sample_pos[chan] += 0x8000;//(sample_increment * (0x800 + ((lfo_p_phase * m_lfo_pmod_depth[chan]) >> (m_lfo_step[chan] & 0x40 ? 18 : 19)))) >> 11;
+ (void)lfo_p_phase;
+ (void)sample_increment;
+
+ if((m_sample_pos[chan] >> 15) >= m_sample_end[chan]) {
+ if(!m_sample_end[chan])
+ m_active[chan] = false;
+ else {
+ s32 prev = m_sample_pos[chan];
+ do
+ m_sample_pos[chan] -= (m_sample_end[chan] << 15) | ((m_sample_dec_and_format[chan] & 0x3f) << 9);
+ while((m_sample_pos[chan] >> 15) >= m_sample_end[chan]);
+ m_dpcm_address[chan] += (m_sample_pos[chan] >> 15) - (prev >> 15);
+ }
+ }
+
+ if(m_lpf_speed[chan] & 0x80)
+ m_lpf_done[chan] = istep(m_lpf_timer[chan], 0, m_global_step[m_lpf_speed[chan] & 0x7f] >> 1);
+ else
+ m_lpf_done[chan] = istep(m_lpf_value[chan], m_lpf_target_value[chan], m_global_step[m_lpf_speed[chan]] >> 1);
+
+ istep(m_glo_level_cur[chan], m_glo_level[chan] << 4, 1);
+ istep(m_pan_l[chan], panmap[m_panning[chan] >> 4], 1);
+ istep(m_pan_r[chan], panmap[m_panning[chan] & 15], 1);
+
+ if(m_decay[chan]) {
+ if((m_decay_speed[chan] & 0x60) == 0x60)
+ m_decay_done[chan] = fpstep(m_envelope_level[chan], m_decay_level[chan] << 20, decay_linear_step[m_decay_speed[chan] & 0x1f]);
+ else
+ m_decay_done[chan] = istep(m_envelope_level[chan], m_decay_level[chan] << 20, m_global_step[m_decay_speed[chan]] << 1);
+ if(m_envelope_level[chan] & 0x8000000)
+ m_active[chan] = false;
+
+ } else if(m_attack_speed[chan] & 0x80)
+ m_decay[chan] = fpstep(m_envelope_level[chan], 0, attack_linear_step[m_attack_speed[chan] & 0x7f]);
+ else
+ m_decay[chan] = istep(m_envelope_level[chan], 0, m_global_step[m_attack_speed[chan]] << 1);
+ }
+
+ dry_l >>= 8;
+ dry_r >>= 8;
+ rev >>= 8;
+ cho_l >>= 8;
+ cho_r >>= 8;
+ var_l >>= 8;
+ var_r >>= 8;
+
+
+ // Variation block
+ // Update the output volume
+ m_var_vol = m9(m_var_vol, 0xbd) + m_const[0xbc];
+
+ // Scale the input
+ var_l = m7(var_l, 0x04);
+ var_r = m7(var_r, 0x07);
+
+ // Split depending on the variant selected
+ s32 var_out_l = 0, var_out_r = 0;
+
+ switch(m_meg_control & 0xc0) {
+ case 0x00: {
+ // Used by:
+ // - 2-band EQ
+ // - Auto Pan
+ // - Celeste
+ // - Chorus
+ // - Delays
+ // - Flanger
+ // - Rotary Speaker
+ // - Symphonic
+ // - Tremolo
+
+ // Two stages of filtering
+ s32 var_filter_l_2 = m7(m_var_filter_l_1, 0x7e) + m7(var_l, 0x7f) + m9(m_var_filter_l_2, 0x80);
+ s32 var_filtered_l = m7(m_var_filter_l_2, 0xa7) + m7(var_filter_l_2, 0xa8) + m9(m_var_filter_l_3, 0xa9);
+
+ m_var_filter_l_1 = var_l;
+ m_var_filter_l_2 = var_filter_l_2;
+ m_var_filter_l_3 = var_filtered_l;
+
+ s32 var_filter_r_2 = m7(m_var_filter_r_1, 0x98) + m7(var_r, 0x99) + m9(m_var_filter_r_2, 0x9a);
+ s32 var_filtered_r = m7(m_var_filter_r_2, 0x9b) + m7(var_filter_r_2, 0x9c) + m9(m_var_filter_r_3, 0x9d);
+
+ m_var_filter_r_1 = var_r;
+ m_var_filter_r_2 = var_filter_r_2;
+ m_var_filter_r_3 = var_filtered_r;
+
+ // Rest is like, complex and stuff
+ lfo_step(m_var_lfo_phase, 0x77);
+ s32 var_lfo_phase_2 = m7(m7(m_var_lfo_phase, 0x6d), 0x70) & 0x7fffff;
+
+ filtered_lfo_step(m_var_lfo1a, m_var_lfo_phase, 0x6e, 0x6f, 0x72, 0x71);
+ filtered_lfo_step(m_var_lfo2a, m_var_lfo_phase, 0x79, 0x7a, 0x7c, 0x7b);
+ filtered_lfo_step(m_var_lfo3a, m_var_lfo_phase, 0x88, 0x89, 0x8b, 0x8a);
+
+ s32 lfo1b = lfo_scale(m_var_lfo1a, 0x73);
+ s32 lfo2b = lfo_scale(m_var_lfo1a, 0x7d);
+ s32 lfo3b = lfo_scale(m_var_lfo1a, 0x8c);
+
+ s32 lfo1c = lfo_wrap_reg(var_lfo_phase_2, 0x74);
+ s32 lfo2c = lfo_wrap_reg(var_lfo_phase_2, 0x84);
+ s32 lfo3c = lfo_wrap_reg(var_lfo_phase_2, 0x8d);
+
+ filtered_lfo_step(m_var_lfo4a, lfo3c, -1, 0x8e, 0x90, 0x8f);
+ s32 lfo4b = lfo_scale(m_var_lfo4a, 0x91);
+
+ s32 tap1 = bvar.rlfo2(0x78, m9(lfo1b, 0x75) + m9(lfo1c, 0x76));
+ s32 tap2 = bvar.rlfo2(0x87, m9(lfo2b, 0x85) + m9(lfo2c, 0x86));
+ s32 tap3 = bvar.rlfo2(0x99, m9(lfo3b, 0x95) + m9(lfo3c, 0x96));
+ s32 tap4 = bvar.rlfo2(0xa8, m9(lfo4b, 0xa5));
+
+ s32 mod1 = lfo_mod(lfo1b, 0x83);
+ s32 mod2 = lfo_mod(lfo2b, 0x94);
+ s32 mod3 = lfo_mod(lfo3b, 0xa4);
+
+ m_var_lfo_h_1 = m9(m_var_lfo_h_1, 0x9e) + m9(tap1, 0x9f);
+ m_var_lfo_h_2 = m9(m_var_lfo_h_2, 0xa0) + m9(tap1, 0xa1);
+
+ bvar.w(0xae, var_filtered_l + m9(var_filtered_r, 0xaa) + m9(m_var_lfo_h_1, 0xab) + m9(m_var_lfo_h_2, 0xac));
+ bvar.w(0xb1, m9(var_filtered_r, 0xad) + m9(m_var_lfo_h_1, 0xae) + m9(m_var_lfo_h_2, 0xaf));
+
+ var_out_l = m9(var_filtered_l, 0xb2) + m9(var_filtered_r, 0xb3) + m9(m9v(tap2, mod1), 0xb4) + m9(m9v(tap3, mod3), 0xb5) + m9(tap4, 0xb6);
+ var_out_r = m9(var_filtered_l, 0xb7) + m9(var_filtered_r, 0xb8) + m9(m9v(tap2, mod2), 0xb9) + m9(m9v(tap3, mod3), 0xba) + m9(tap4, 0xbb);
+
+ break;
+ }
+
+ case 0x40: {
+ // Used by:
+ // - Phaser
+
+ // Two stages of filtering
+ s32 var_filter_l_2 = m7(m_var_filter_l_1, 0x6d) + m7(var_l, 0x6e) + m9(m_var_filter_l_2, 0x6f);
+ s32 var_filtered_l = m7(m_var_filter_l_2, 0x70) + m7(var_filter_l_2, 0x71) + m9(m_var_filter_l_3, 0x72);
+
+ m_var_filter_l_1 = var_l;
+ m_var_filter_l_2 = var_filter_l_2;
+ m_var_filter_l_3 = var_filtered_l;
+ s32 var_filter_r_2 = m7(m_var_filter_r_1, 0x73) + m7(var_r, 0x74) + m9(m_var_filter_r_2, 0x75);
+ s32 var_filtered_r = m7(m_var_filter_r_2, 0x76) + m7(var_filter_r_2, 0x77) + m9(m_var_filter_r_3, 0x78);
+
+ m_var_filter_r_1 = var_r;
+ m_var_filter_r_2 = var_filter_r_2;
+ m_var_filter_r_3 = var_filtered_r;
+
+ // A very funky amplitude lfo with a lot of stages
+ s32 var_raw_l = m9(m_var_filterp_l_4, 0x7b) + m9(m_var_filterp_l_5, 0x7c) + m9(m_var_filterp_l_6, 0x7d);
+ s32 var_raw_r = m9(m_var_filterp_r_4, 0x7e) + m9(m_var_filterp_r_5, 0x7f) + m9(m_var_filterp_r_6, 0x80);
+
+ s32 var_o_l = m9(var_raw_l, 0xa3) + m9(m_var_filterp_r_3, 0xa4) + m9(m_var_filterp_r_5, 0xa5);
+ s32 var_o_r = m9(var_raw_r, 0xa7);
+
+ lfo_step(m_var_lfo_phase, 0x79);
+ s32 alfo_l = 0x200 - alfo(m_var_lfo_phase, 0, 0x83, 0x82, false);
+ s32 alfo_r = 0x200 - alfo(m_var_lfo_phase, m_const[0x9c], 0x9e, 0x9d, false);
+
+ s32 var_l_1 = m9(var_filtered_l, 0x84) + m9(var_filtered_r, 0x85) + m9(var_raw_l, 0x86) + m9(var_raw_r, 0x87);
+ s32 var_l_2 = m_var_filterp_l_1 + m9v(m_var_filterp_l_2 - var_l_1, alfo_l);
+ m_var_filterp_l_1 = var_l_1;
+ s32 var_l_3 = m_var_filterp_l_2 + m9v(m_var_filterp_l_3 - var_l_2, alfo_l);
+ m_var_filterp_l_2 = var_l_2;
+ s32 var_l_4 = m_var_filterp_l_3 + m9v(m_var_filterp_l_4 - var_l_3, alfo_l);
+ m_var_filterp_l_3 = var_l_3;
+ s32 var_l_5 = m_var_filterp_l_4 + m9v(m_var_filterp_l_5 - var_l_4, alfo_l);
+ m_var_filterp_l_4 = var_l_4;
+ m_var_filterp_l_6 = m_var_filterp_l_5 + m9v(m_var_filterp_l_6 - var_l_5, alfo_l);
+ m_var_filterp_l_5 = var_l_5;
+
+ s32 var_r_1 = m9(var_filtered_r, 0x9f) + m9(var_raw_l, 0xa0) + m9(var_raw_r, 0xa1);
+ s32 var_r_2 = m_var_filterp_r_1 + m9v(m_var_filterp_r_2 - var_r_1, alfo_r);
+ m_var_filterp_r_1 = var_r_1;
+ s32 var_r_3 = m_var_filterp_r_2 + m9v(m_var_filterp_r_3 - var_r_2, alfo_r);
+ m_var_filterp_r_2 = var_r_2;
+ s32 var_r_4 = m_var_filterp_r_3 + m9v(m_var_filterp_r_4 - var_r_3, alfo_r);
+ m_var_filterp_r_3 = var_r_3;
+ s32 var_r_5 = m_var_filterp_r_4 + m9v(m_var_filterp_r_5 - var_r_4, alfo_r);
+ m_var_filterp_r_4 = var_r_4;
+ m_var_filterp_r_6 = m_var_filterp_r_5 + m9v(m_var_filterp_r_6 - var_r_5, alfo_r);
+ m_var_filterp_r_5 = var_r_5;
+
+ var_out_l = var_o_l + m9(var_filtered_l, 0xa2);
+ var_out_r = var_o_r + m9(var_filtered_r, 0xa6);
+ break;
+ }
+
+ case 0x80: {
+ // Used by:
+ // - 3-band EQ
+ // - Amp simulation
+ // - Distortion
+ // - Gating
+
+ // Compute a center value
+ s32 var_m = m9(var_l, 0x6d) + m9(var_r, 0x6e);
+
+ // Two stages of filtering on the center value
+ s32 var_filter_2 = m7(m_var_filter_1, 0x6f) + m7(var_m, 0x70) + m9(m_var_filter_2, 0x71);
+ s32 var_filtered = m7(m_var_filter_2, 0x72) + m7(var_filter_2, 0x73) + m9(m_var_filter_3, 0x74);
+
+ m_var_filter_1 = var_m;
+ m_var_filter_2 = var_filter_2;
+ m_var_filter_3 = var_filtered;
+
+ // Gating/ER reverb injection with some filtering
+ bvar.w(0x7e, m9(bvar.r(0x6c), 0x7b) + m9(var_m, 0x7c));
+ s32 tap0 = m7(bvar.r(0x6c), 0x7e) + m7(var_m, 0x7f);
+ bvar.w(0x84, m9(bvar.r(0x78), 0x81) + m9(tap0, 0x82));
+
+ s32 var_f3_1 = bvar.r(0x6f);
+ s32 var_f3_2 = m7(m_var_filter2_1, 0x77) + m7(var_f3_1, 0x78) + m9(m_var_filter3_2, 0x79);
+ bvar.w(0x87, m7(bvar.r(0x78), 0x84) + m7(tap0, 0x85) + m9(var_f3_2, 0x86));
+
+ m_var_filter3_1 = var_f3_1;
+ m_var_filter3_2 = var_f3_2;
+
+ // Multi-tap on reverb
+ s32 tap1 = m9(bvar.r(0x6f), 0x99) + m9(bvar.r(0x72), 0x9a) + m9(bvar.r(0x75), 0x9b) + m9(bvar.r(0x8d), 0x9c) + m9(bvar.r(0x90), 0x9d) + m9(bvar.r(0x93), 0x9e) + m9(bvar.r(0x96), 0x9f);
+ s32 tap2 = m9(bvar.r(0x9f), 0xb4) + m9(bvar.r(0xa2), 0xb5) + m9(bvar.r(0xa5), 0xb6) + m9(bvar.r(0xa8), 0xb7) + m9(bvar.r(0xab), 0xb8) + m9(bvar.r(0xae), 0xb9) + m9(bvar.r(0xb1), 0xba);
+
+ bvar.w(0xb7, tap1);
+ bvar.w(0xba, tap2);
+
+ s32 tap2b = tap2 + m9(brev.r(0xb4), 0xbb);
+ bvar.w(0x8a, m9(bvar.r(0x7b), 0x88) + m9(tap2b, 0x89));
+ s32 var_gate_l = m7(bvar.r(0x7b), 0x8b) + m7(tap2b, 0x8c);
+
+ s32 tap1b = tap1 + m9(brev.r(0x99), 0xa0);
+ bvar.w(0x9c, m9(bvar.r(0x81), 0x8e) + m9(tap1b, 0x8f));
+ s32 var_gate_r = m7(bvar.r(0x7b), 0x8b) + m7(tap1b, 0x8c);
+
+ // Distortion stage
+ s32 dist1 = saturate(m7(var_filtered, 0x76));
+ s32 dist2 = saturate(m7(dist1, 0x83));
+ s32 dist3 = saturate(m7(dist2, 0x87));
+ s32 dist4 = saturate(m7(dist3, 0x8a));
+ s32 dist5 = saturate(m7(dist4, 0x8d));
+ s32 dist6 = saturate(m7(dist5, 0x90));
+ s32 disto = m9(m9(dist1, 0x91) + m9(dist2, 0x92) + m9(dist3, 0x93) + m9(dist4, 0x94) + m9(dist5, 0x95) + m9(dist6, 0x96), 0xa1);
+
+ // Filtering again, 3 stages
+ s32 var_f2_2 = m7(m_var_filter2_1, 0xa2) + m7(disto, 0xa3) + m9(m_var_filter2_2a, 0xa4);
+ s32 var_f2_3 = m7(m_var_filter2_3b, 0xa5) + m7(m_var_filter2_3a, 0xa6) + m7(m_var_filter2_2b, 0xa7) + m7(m_var_filter2_2a, 0xa8) + m7(var_f2_2, 0xa9);
+ s32 var_f2_4 = m7(m_var_filter2_3a, 0xaa) + m7(var_f2_3, 0xab) + m9(m_var_filter2_4, 0xac);
+
+ m_var_filter2_1 = disto;
+ m_var_filter2_2b = m_var_filter2_2a;
+ m_var_filter2_2a = var_f2_2;
+ m_var_filter2_3b = m_var_filter2_3a;
+ m_var_filter2_3a = var_f2_3;
+ m_var_filter2_4 = var_f2_4;
+
+ // Mix in both paths
+ var_out_l = m9(var_l, 0xad) + m9(var_gate_l, 0xaf) + m9(var_f2_4, 0xb0);
+ var_out_r = m9(var_r, 0xb1) + m9(var_gate_r, 0xb2) + m9(var_f2_4, 0xb3);
+
+ break;
+ }
+
+ case 0xc0: {
+ // Used by:
+ // - Auto wah
+ // - Hall
+ // - Karaoke
+ // - Plate
+ // - Room
+ // - Stage
+
+ // Compute a center value
+ s32 var_m = m9(var_l, 0x6d) + m9(var_r, 0x6e);
+
+ // Two stages of filtering on the center value
+ s32 var_filter_2 = m7(m_var_filter_1, 0x6f) + m7(var_m, 0x70) + m9(m_var_filter_2, 0x71);
+ s32 var_filtered = m7(m_var_filter_2, 0x72) + m7(var_filter_2, 0x73) + m9(m_var_filter_3, 0x74);
+ m_var_filter_1 = var_m;
+ m_var_filter_2 = var_filter_2;
+ m_var_filter_3 = var_filtered;
+
+ // Inject in the reverb buffer and loop with filtering
+ s32 tap1a = bvar.r(0x6c); // 36 v19
+ s32 tap1b = bvar.r(0x6f); // 37 v21
+ s32 tap1c = bvar.r(0x72); // 38 v27
+
+ bvar.w(0x75, var_filtered + m9(tap1a, 0x75));
+ bvar.w(0x78, m9(tap1b, 0x76) + m9(tap1a, 0x77));
+
+ s32 tap2a = m7(tap1b, 0x78) + m7(tap1a, 0x79);
+
+ bvar.w(0x7b, m9(tap1b, 0x7a) + m9(tap2a, 0x7b));
+
+ s32 tap2b = m7(tap1c, 0x7c) + m7(tap2a, 0x7d);
+
+ s32 tap1d = bvar.r(0x9c);
+ s32 tap1e = bvar.r(0x9f);
+
+ bvar.w(0xa8, m9(m_var_h1, 0xa5) + m9(tap1d, 0xa6) + m9(tap2b, 0xa7));
+ m_var_h1 = tap1d;
+
+ bvar.w(0xae, m9(m_var_h2, 0xa8) + m9(tap1e, 0xa9) + m9(tap2b, 0xaa));
+ m_var_h2 = tap1e;
+
+ s32 tap1f = bvar.r(0xab);
+ s32 tap1g = bvar.r(0xb1);
+
+ bvar.w(0xb7, m9(m_var_h3, 0xb3) + m9(tap1f, 0xb4) + m9(tap2b, 0xb5));
+ m_var_h3 = tap1f;
+
+ bvar.w(0xba, m9(m_var_h4, 0xb6) + m9(tap1g, 0xb7) + m9(tap2b, 0xb8));
+ m_var_h4 = tap1g;
+
+ s32 tap1h = bvar.r(0x7e);
+
+ s32 tap3a = m9(bvar.r(0x81) + bvar.r(0x84) + bvar.r(0x87) + bvar.r(0x8a), 0x8f) + m9(tap1h, 0x93);
+ s32 tap3b = bvar.r(0xa5);
+ bvar.w(0xb4, m9(tap3b, 0xaf) + m9(tap3a, 0xb0));
+ s32 var_o_l = m7(tap3b, 0xb1) + m7(tap3a, 0xb2);
+
+ s32 tap4a = m9(bvar.r(0x8d) + bvar.r(0x90) + bvar.r(0x93) + bvar.r(0x96), 0x9c) + m9(tap1h, 0xa0);
+ s32 tap4b = bvar.r(0x99);
+ bvar.w(0xa2, m9(tap4b, 0xa1) + m9(tap4a, 0xa2));
+ s32 var_o_r = m7(tap4b, 0xa3) + m7(tap4a, 0xa4);
+
+ // auto-wah effect with lfo
+ // Two stages of filtering
+ s32 var_filter_l_2 = m7(m_var_filter_l_1, 0x80) + m7(var_l, 0x81) + m9(m_var_filter_l_2, 0x82);
+ s32 var_filtered_l = m7(m_var_filter_l_2, 0x83) + m7(var_filter_l_2, 0x84) + m9(m_var_filter_l_3, 0x85);
+
+ m_var_filter_l_1 = var_l;
+ m_var_filter_l_2 = var_filter_l_2;
+ m_var_filter_l_3 = var_filtered_l;
+ s32 var_filter_r_2 = m7(m_var_filter_r_1, 0x6f) + m7(var_r, 0x70) + m9(m_var_filter_r_2, 0x71);
+ s32 var_filtered_r = m7(m_var_filter_r_2, 0x72) + m7(var_filter_r_2, 0x73) + m9(m_var_filter_r_3, 0x74);
+
+ m_var_filter_r_1 = var_r;
+ m_var_filter_r_2 = var_filter_r_2;
+ m_var_filter_r_3 = var_filtered_r;
+
+ // Mixing
+ s32 var_w_l = m7(var_filtered_l, 0x94) + m7(var_filtered_r, 0x95);
+ s32 var_w_r = m7(var_filtered_r, 0x88);
+
+ // Amplitude LFO and filtering
+ lfo_step(m_var_lfo_phase, 0x7e);
+ s32 amp = alfo(m_var_lfo_phase, 0, 0x86, 0x87, true);
+
+ m_var_filterp_l_1 = m9v(m9(m_var_filterp_l_1, 0x89) + m9(m_var_filterp_l_2, 0x8a) + var_w_l, amp) + m9(m_var_filterp_l_1, 0x8b);
+ m_var_filterp_l_2 = m9v(m_var_filterp_l_1, amp) + m9(m_var_filterp_l_2, 0x8d);
+
+ m_var_filterp_r_1 = m9v(m9(m_var_filterp_r_1, 0x96) + m9(m_var_filterp_r_2, 0x97) + var_w_r, amp) + m9(m_var_filterp_r_1, 0x98);
+ m_var_filterp_r_2 = m9v(m_var_filterp_r_1, amp) + m9(m_var_filterp_r_2, 0x9a);
+
+ var_out_l = m9(var_filtered_l, 0xb9) + m9(m_var_filterp_l_1, 0xba) + m9(var_o_l, 0xbb);
+ var_out_r = m9(var_filtered_r, 0xab) + m9(var_r, 0xac) + m9(m_var_filterp_r_1, 0xad) + m9(var_o_r, 0xae);
+ break;
+ }
+ }
+
+
+ // Chorus block
+ // Update the output volume
+ m_cho_vol = m9(m_cho_vol, 0x58) + m_const[0x57];
+
+ // Scale the input
+ cho_l = m7(cho_l, 0x02);
+ cho_r = m7(cho_r, 0x05);
+
+ // Add in the other channels
+ cho_l += m9v(m7(var_out_l, 0x03), m_var_vol);
+ cho_r += m9v(m7(var_out_r, 0x06), m_var_vol);
+
+ // A LFO with (up to) three phases to pick up the reverb
+ lfo_step(m_cho_lfo_phase, 0x09);
+
+ s32 cho_lfo_1 = bcho.rlfo(0x1b, m_cho_lfo_phase, 0, 0x1a);
+ s32 cho_lfo_2 = bcho.rlfo(0x2a, m_cho_lfo_phase, m_const[0x25], 0x28);
+ s32 cho_lfo_3 = bcho.rlfo(0x39, m_cho_lfo_phase, m_const[0x34], 0x37);
+
+ // Two stages of filtering
+ s32 cho_filter_r_2 = m7(m_cho_filter_r_1, 0x3c) + m7(cho_r, 0x3d) + m9(m_cho_filter_r_2, 0x3e);
+ s32 cho_filtered_r = m7(m_cho_filter_r_2, 0x3f) + m7(cho_filter_r_2, 0x40) + m9(m_cho_filter_r_3, 0x41);
+
+ m_cho_filter_r_1 = cho_r;
+ m_cho_filter_r_2 = cho_filter_r_2;
+ m_cho_filter_r_3 = cho_filtered_r;
+
+ s32 cho_filter_l_2 = m7(m_cho_filter_l_1, 0x49) + m7(cho_l, 0x4a) + m9(m_cho_filter_l_2, 0x4b);
+ s32 cho_filtered_l = m7(m_cho_filter_l_2, 0x4c) + m7(cho_filter_l_2, 0x4d) + m9(m_cho_filter_l_3, 0x4e);
+
+ m_cho_filter_l_1 = cho_l;
+ m_cho_filter_l_2 = cho_filter_l_2;
+ m_cho_filter_l_3 = cho_filtered_l;
+
+ // Reverb feedback from there, slighly assymetric to cover more possibilities
+ bcho.w(0x42, m9(cho_lfo_2, 0x42) + cho_filtered_r);
+ bcho.w(0x51, m9(cho_lfo_1, 0x4f) + cho_filtered_l + m9(cho_filtered_r, 0x50));
+
+ // Final value by combining the LFO-ed reverbs
+ s32 cho_out_l = m9(cho_lfo_1, 0x60) + m9(cho_lfo_3, 0x61);
+ s32 cho_out_r = m9(cho_lfo_2, 0x69) + m9(cho_lfo_3, 0x6a);
+
+
+
+ // Reverb block
+ // Update the output volume
+ m_rev_vol = m9(m_rev_vol, 0x0c) + m_const[0x0b];
+
+ // Scale the input
+ rev = m7(rev, 0x11);
+
+ // Add in the other channels
+ rev += m9v(m7(cho_out_l, 0x12) + m7(cho_out_r, 0x13), m_cho_vol);
+ rev += m9v(m7(var_out_l, 0x14) + m7(var_out_r, 0x15), m_var_vol);
+
+ // Two stages of filtering (hpf then lpf)
+ s32 rev_filter_2 = m7(m_rev_filter_1, 0x2d) + m7(rev, 0x2e) + m9(m_rev_filter_2, 0x2f);
+ s32 rev_filtered = m7(m_rev_filter_2, 0x30) + m7(rev_filter_2, 0x31) + m9(m_rev_filter_3, 0x32);
+
+ m_rev_filter_1 = rev;
+ m_rev_filter_2 = rev_filter_2;
+ m_rev_filter_3 = rev_filtered;
+
+ // Main reverb
+ brev.w(0x30, m9(brev.r(0x21), 0x29) + m9(brev.r(0x18), 0x2a));
+ brev.w(0x33, m9(brev.r(0x1b), 0x33) + rev_filtered);
+
+ // Second dual reverb
+ s32 rev_1 = m7(brev.r(0x33), 0x2b) + m7(brev.r(0x18), 0x2c);
+ s32 rev_2 = m7(brev.r(0x27), 0x3a) + m7(rev_1, 0x3b);
+ brev.w(0x3f, m9(brev.r(0x39), 0x38) + m9(rev_1, 0x39));
+
+ // Four more parallel layers with filtering
+ brev.w(0x5d, m9(m_rev_hist_a, 0x59) + m9(brev.r(0x24), 0x5a) + m9(rev_2, 0x5b));
+ m_rev_hist_a = brev.r(0x24);
+ brev.w(0x63, m9(m_rev_hist_b, 0x5c) + m9(brev.r(0x54), 0x5d) + m9(rev_2, 0x5e));
+ m_rev_hist_b = brev.r(0x54);
+ brev.w(0x69, m9(m_rev_hist_c, 0x62) + m9(brev.r(0x5a), 0x63) + m9(rev_2, 0x64));
+ m_rev_hist_c = brev.r(0x63);
+ brev.w(0x6c, m9(m_rev_hist_d, 0x65) + m9(brev.r(0x60), 0x66) + m9(rev_2, 0x67));
+ m_rev_hist_d = brev.r(0x66);
+
+ // Split final pick-up and injection
+ s32 rev_base_l = m9(brev.r(0x00) + brev.r(0x03) + brev.r(0x06) + brev.r(0x09), 0x1c) + m9(brev.r(0xbd), 0x1b);
+ brev.w(0x48, m9(brev.r(0x36), 0x45) + m9(rev_base_l, 0x46));
+ s32 rev_out_l = m7(brev.r(0x36), 0x47) + m7(rev_base_l, 0x48);
+
+ s32 rev_base_r = m9(brev.r(0x0c) + brev.r(0x0f) + brev.r(0x12) + brev.r(0x15), 0x21) + m9(brev.r(0xbd), 0x20);
+ brev.w(0x48, m9(brev.r(0x36), 0x51) + m9(rev_base_r, 0x52));
+ s32 rev_out_r = m7(brev.r(0x36), 0x53) + m7(rev_base_r, 0x54);
+
+
+ // Scale the dry input
+ dry_l = m7(dry_l, 0xbe);
+ dry_r = m7(dry_r, 0x01);
+
+
+ // Add in the other channels
+ dry_l += m9v(rev_out_l, m_rev_vol) + m9v(m9(cho_out_l, 0x17), m_cho_vol) + m9v(m9(var_out_l, 0x18), m_var_vol);
+ dry_r += m9v(rev_out_r, m_rev_vol) + m9v(m9(cho_out_r, 0x0e), m_cho_vol) + m9v(m9(var_out_r, 0x0f), m_var_vol);
+
+ outputs[0].put_int(i, dry_l, 32768);
+ outputs[1].put_int(i, dry_r, 32768);
+
+ m_buffer_offset --;
+ }
}
diff --git a/src/devices/sound/swp00.h b/src/devices/sound/swp00.h
index ae732979606..21b7b384085 100644
--- a/src/devices/sound/swp00.h
+++ b/src/devices/sound/swp00.h
@@ -11,7 +11,7 @@
#include "meg.h"
#include "dirom.h"
-class swp00_device : public device_t, public device_sound_interface, public device_rom_interface<25+2, 2, 0, ENDIANNESS_LITTLE>
+class swp00_device : public device_t, public device_sound_interface, public device_rom_interface<24, 0, 0, ENDIANNESS_LITTLE>
{
public:
swp00_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 33868800);
@@ -22,26 +22,225 @@ protected:
virtual void device_start() override;
virtual void device_reset() override;
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
virtual void device_add_mconfig(machine_config &config) override;
private:
- // required_device<meg_embedded_device> m_meg;
+ template<size_t size> struct delay_block {
+ swp00_device *m_swp;
+ std::array<s32, size> &m_buffer;
+
+ delay_block(swp00_device *swp, std::array<s32, size> &buffer);
+ s32 r(int offreg) const;
+ s32 rlfo(int offreg, u32 phase, s32 delta_phase, int levelreg) const;
+ s32 rlfo2(int offreg, s32 offset) const;
+ void w(int offreg, s32 value) const;
+ };
sound_stream *m_stream;
+ static const std::array<s32, 0x80> attack_linear_step;
+ static const std::array<s32, 0x20> decay_linear_step;
+ static const std::array<s32, 16> panmap;
+ std::array<s32, 0x80> m_global_step;
+ std::array<s16, 0x100> m_dpcm;
+
+ static const std::array<u32, 4> lfo_shape_centered_saw;
+ static const std::array<u32, 4> lfo_shape_centered_tri;
+ static const std::array<u32, 4> lfo_shape_offset_saw;
+ static const std::array<u32, 4> lfo_shape_offset_tri;
+
+ // MEG reverb memory
+ std::array<s32, 0x20000> m_rev_buffer;
+ std::array<s32, 0x8000> m_cho_buffer;
+ std::array<s32, 0x20000> m_var_buffer;
+
+ // MEG registers
+ std::array<u16, 0x40> m_offset;
+ std::array<u16, 0xc0> m_const;
+
+ // AWM registers
+ std::array<u16, 0x20> m_lpf_info;
+ std::array<u8, 0x20> m_lpf_speed;
+ std::array<u8, 0x20> m_lfo_famod_depth;
+ std::array<u8, 0x20> m_rev_level;
+ std::array<u8, 0x20> m_dry_level;
+ std::array<u8, 0x20> m_cho_level;
+ std::array<u8, 0x20> m_var_level;
+ std::array<u8, 0x20> m_glo_level;
+ std::array<u8, 0x20> m_panning;
+ std::array<u8, 0x20> m_attack_speed;
+ std::array<u8, 0x20> m_attack_level;
+ std::array<u8, 0x20> m_decay_speed;
+ std::array<u8, 0x20> m_decay_level;
+ std::array<u16, 0x20> m_pitch;
+ std::array<u16, 0x20> m_sample_start;
+ std::array<u16, 0x20> m_sample_end;
+ std::array<u8, 0x20> m_sample_dec_and_format;
+ std::array<u32, 0x20> m_sample_address;
+ std::array<u8, 0x20> m_lfo_step;
+ std::array<u8, 0x20> m_lfo_pmod_depth;
+
+ std::array<u32, 0x20> m_lfo_phase;
+ std::array<s32, 0x20> m_sample_pos;
+ std::array<s32, 0x20> m_envelope_level;
+ std::array<s32, 0x20> m_glo_level_cur;
+ std::array<s32, 0x20> m_pan_l;
+ std::array<s32, 0x20> m_pan_r;
+ std::array<s32, 0x20> m_lpf_feedback;
+ std::array<s32, 0x20> m_lpf_target_value;
+ std::array<s32, 0x20> m_lpf_value;
+ std::array<s32, 0x20> m_lpf_timer;
+ std::array<s32, 0x20> m_lpf_ha;
+ std::array<s32, 0x20> m_lpf_hb;
+ std::array<bool, 0x20> m_active, m_decay, m_decay_done, m_lpf_done;
+ std::array<s16, 0x20> m_dpcm_current;
+ std::array<s16, 0x20> m_dpcm_next;
+ std::array<u32, 0x20> m_dpcm_address;
+
+ u16 m_waverom_val;
+ u8 m_waverom_access;
+ u8 m_state_adr;
+ u8 m_meg_control;
+
+ // MEG state
+ u32 m_buffer_offset;
+ s32 m_rev_vol, m_cho_vol, m_var_vol;
+
+ u32 m_var_lfo_phase;
+
+ s32 m_var_lfo_h_1, m_var_lfo_h_2;
+ s32 m_var_lfo1a, m_var_lfo2a, m_var_lfo3a, m_var_lfo4a;
+
+ s32 m_var_filter_1, m_var_filter_2, m_var_filter_3;
+ s32 m_var_filter_l_1, m_var_filter_l_2, m_var_filter_l_3;
+ s32 m_var_filter_r_1, m_var_filter_r_2, m_var_filter_r_3;
+ s32 m_var_filter2_1, m_var_filter2_2a, m_var_filter2_2b, m_var_filter2_3a, m_var_filter2_3b, m_var_filter2_4;
+ s32 m_var_filter3_1, m_var_filter3_2;
+
+ s32 m_var_filterp_l_1, m_var_filterp_l_2, m_var_filterp_l_3;
+ s32 m_var_filterp_l_4, m_var_filterp_l_5, m_var_filterp_l_6;
+ s32 m_var_filterp_r_1, m_var_filterp_r_2, m_var_filterp_r_3;
+ s32 m_var_filterp_r_4, m_var_filterp_r_5, m_var_filterp_r_6;
+
+ s32 m_var_h1, m_var_h2, m_var_h3, m_var_h4;
+
+ u32 m_cho_lfo_phase;
+ s32 m_cho_filter_l_1, m_cho_filter_l_2, m_cho_filter_l_3;
+ s32 m_cho_filter_r_1, m_cho_filter_r_2, m_cho_filter_r_3;
+
+ s32 m_rev_filter_1, m_rev_filter_2, m_rev_filter_3;
+ s32 m_rev_hist_a, m_rev_hist_b, m_rev_hist_c, m_rev_hist_d;
+
+
+ // Voice control
+
+ template<int sel> void lpf_info_w(offs_t offset, u8 data);
+ template<int sel> u8 lpf_info_r(offs_t offset);
+ void lpf_speed_w(offs_t offset, u8 data);
+ u8 lpf_speed_r(offs_t offset);
+ void lfo_famod_depth_w(offs_t offset, u8 data);
+ u8 lfo_famod_depth_r(offs_t offset);
+ void rev_level_w(offs_t offset, u8 data);
+ u8 rev_level_r(offs_t offset);
+ void dry_level_w(offs_t offset, u8 data);
+ u8 dry_level_r(offs_t offset);
+ void cho_level_w(offs_t offset, u8 data);
+ u8 cho_level_r(offs_t offset);
+ void var_level_w(offs_t offset, u8 data);
+ u8 var_level_r(offs_t offset);
+ void glo_level_w(offs_t offset, u8 data);
+ u8 glo_level_r(offs_t offset);
+ void panning_w(offs_t offset, u8 data);
+ u8 panning_r(offs_t offset);
+ void attack_speed_w(offs_t offset, u8 data);
+ u8 attack_speed_r(offs_t offset);
+ void attack_level_w(offs_t offset, u8 data);
+ u8 attack_level_r(offs_t offset);
+ void decay_speed_w(offs_t offset, u8 data);
+ u8 decay_speed_r(offs_t offset);
+ void decay_level_w(offs_t offset, u8 data);
+ u8 decay_level_r(offs_t offset);
+ template<int sel> void pitch_w(offs_t offset, u8 data);
+ template<int sel> u8 pitch_r(offs_t offset);
+ template<int sel> void sample_start_w(offs_t offset, u8 data);
+ template<int sel> u8 sample_start_r(offs_t offset);
+ template<int sel> void sample_end_w(offs_t offset, u8 data);
+ template<int sel> u8 sample_end_r(offs_t offset);
+ void sample_dec_and_format_w(offs_t offset, u8 data);
+ u8 sample_dec_and_format_r(offs_t offset);
+ template<int sel> void sample_address_w(offs_t offset, u8 data);
+ template<int sel> u8 sample_address_r(offs_t offset);
+ void lfo_step_w(offs_t offset, u8 data);
+ u8 lfo_step_r(offs_t offset);
+ void lfo_pmod_depth_w(offs_t offset, u8 data);
+ u8 lfo_pmod_depth_r(offs_t offset);
+
+ void slot8_w(offs_t offset, u8 data);
+ void slot9_w(offs_t offset, u8 data);
+
+ // Internal state access
+ u8 state_r();
+ void state_adr_w(u8 data);
+
+ // MEG
+ void offset_w(offs_t offset, u8 data);
+ u8 offset_r(offs_t offset);
+ void const_w(offs_t offset, u8 data);
+ u8 const_r(offs_t offset);
+ void meg_control_w(u8 data);
+ u8 meg_control_r();
+
+ // Control registers
+ void keyon(int chan);
+ template<int sel> void keyon_w(u8 data);
+
+ void waverom_access_w(u8 data);
+ u8 waverom_access_r();
+ u8 waverom_val_r();
+
// Generic catch-all
u8 snd_r(offs_t offset);
void snd_w(offs_t offset, u8 data);
- inline auto &rchan(address_map &map, int idx) {
- return map(idx*2, idx*2+1).select(0x7c0);
+ inline auto &rctrl(address_map &map, int idx) {
+ return map(idx, idx);
}
- inline auto &rctrl(address_map &map, int idx) {
- int slot = 0x20*(idx >> 1) | 0xe | (idx & 1);
- return map(slot*2, slot*2+1);
+ inline auto &rchan(address_map &map, int idx) {
+ int slot = ((idx & 0x3e) << 5) | (idx & 1);
+ return map(slot, slot).select(0x3e);
}
+
+ // Other methods
+ static bool istep(s32 &value, s32 limit, s32 step);
+ static bool fpstep(s32 &value, s32 limit, s32 step);
+ static s32 fpadd(s32 value, s32 step);
+ static s32 fpsub(s32 value, s32 step);
+ static s32 fpapply(s32 value, s32 sample);
+ static s32 lpffpapply(s32 value, s32 sample);
+
+ s32 rext(int reg) const;
+ static s32 m7v(s32 value, s32 mult);
+ s32 m7(s32 value, int reg) const;
+ static s32 m9v(s32 value, s32 mult);
+ s32 m9(s32 value, int reg) const;
+ s32 lfo_get_step(int reg) const;
+ void lfo_step(u32 &phase, int reg) const;
+ static u32 lfo_wrap(s32 phase, s32 delta);
+ static s32 lfo_saturate(s32 phase);
+ s32 lfo_wrap_reg(s32 phase, int deltareg) const;
+ void filtered_lfo_step(s32 &position, s32 phase, int deltareg, int postdeltareg, int scalereg, int feedbackreg);
+ s32 lfo_mod(s32 phase, int scalereg) const;
+ s32 lfo_scale(s32 phase, int scalereg) const;
+
+ s32 alfo(u32 phase, s32 delta_phase, int levelreg, int offsetreg, bool sub) const;
+
+ s32 sx(int reg) const;
+ double sx7(int reg) const;
+ double sx9(int reg) const;
+
+ static s32 saturate(s32 value);
};
DECLARE_DEVICE_TYPE(SWP00, swp00_device)
diff --git a/src/devices/sound/swp20.cpp b/src/devices/sound/swp20.cpp
index 221f5955d0b..947d2bb8b0a 100644
--- a/src/devices/sound/swp20.cpp
+++ b/src/devices/sound/swp20.cpp
@@ -17,64 +17,184 @@ swp20_device::swp20_device(const machine_config &mconfig, const char *tag, devic
void swp20_device::device_start()
{
+ m_stream = stream_alloc(0, 2, 44100);
+
+ save_item(NAME(m_sample_address));
}
void swp20_device::device_reset()
{
- m_p3c_port = 0x00;
- m_p3c_address = true;
+ std::fill(m_sample_address.begin(), m_sample_address.end(), 0);
+
+ m_waverom_access = 0;
+ m_waverom_val = 0;
+
+ m_eq_port = 0x00;
+ m_eq_address = true;
m_voice = 0x00;
m_keyon = 0;
m_keyoff = 0;
}
-void swp20_device::rom_bank_updated()
+void swp20_device::map(address_map &map)
+{
+ map(0x00, 0x3f).rw(FUNC(swp20_device::snd_r), FUNC(swp20_device::snd_w));
+
+ map(0x01, 0x01).w(FUNC(swp20_device::voice_w));
+
+ map(0x10, 0x10).rw(FUNC(swp20_device::pitch_r<1>), FUNC(swp20_device::pitch_w<1>));
+ map(0x11, 0x11).rw(FUNC(swp20_device::pitch_r<0>), FUNC(swp20_device::pitch_w<0>));
+
+ map(0x14, 0x14).rw(FUNC(swp20_device::pan_l_r), FUNC(swp20_device::pan_l_w));
+ map(0x15, 0x15).rw(FUNC(swp20_device::pan_r_r), FUNC(swp20_device::pan_r_w));
+
+ map(0x26, 0x26).rw(FUNC(swp20_device::sample_start_r<2>), FUNC(swp20_device::sample_start_w<2>));
+ map(0x27, 0x27).rw(FUNC(swp20_device::sample_start_r<1>), FUNC(swp20_device::sample_start_w<1>));
+ map(0x28, 0x28).rw(FUNC(swp20_device::sample_start_r<0>), FUNC(swp20_device::sample_start_w<0>));
+ map(0x29, 0x29).rw(FUNC(swp20_device::sample_format_r), FUNC(swp20_device::sample_format_w));
+ map(0x2a, 0x2a).rw(FUNC(swp20_device::sample_end_r<1>), FUNC(swp20_device::sample_end_w<1>));
+ map(0x2b, 0x2b).rw(FUNC(swp20_device::sample_end_r<0>), FUNC(swp20_device::sample_end_w<0>));
+ map(0x2c, 0x2c).rw(FUNC(swp20_device::sample_address_r<3>), FUNC(swp20_device::sample_address_w<3>));
+ map(0x2d, 0x2d).rw(FUNC(swp20_device::sample_address_r<2>), FUNC(swp20_device::sample_address_w<2>));
+ map(0x2e, 0x2e).rw(FUNC(swp20_device::sample_address_r<1>), FUNC(swp20_device::sample_address_w<1>));
+ map(0x2f, 0x2f).rw(FUNC(swp20_device::sample_address_r<0>), FUNC(swp20_device::sample_address_w<0>));
+
+ map(0x37, 0x37).w(FUNC(swp20_device::waverom_access_w));
+ map(0x3a, 0x3a).r(FUNC(swp20_device::waverom_val_r<1>));
+ map(0x3b, 0x3b).r(FUNC(swp20_device::waverom_val_r<0>));
+
+ map(0x3c, 0x3c).w(FUNC(swp20_device::eq_w));
+}
+
+void swp20_device::voice_w(u8 data)
{
+ // Code uses 20-3f for voices on the second swp, it looks like
+ // just leaking internal information and the top bits are not
+ // significant
+
+ m_voice = data & 0x1f;
}
-void swp20_device::map(address_map &map)
+void swp20_device::waverom_access_w(u8 data)
{
- map(0x00, 0x3f).rw(FUNC(swp20_device::snd_r), FUNC(swp20_device::snd_w));
+ m_waverom_access = data;
+}
- map(0x3c, 0x3c).w(FUNC(swp20_device::p3c_w));
+template<int sel> u8 swp20_device::waverom_val_r()
+{
+ return read_word(m_sample_address[0x1f]*2) >> (8*sel);
+}
+
+void swp20_device::pan_l_w(u8 data)
+{
+ m_stream->update();
+ m_pan_l[m_voice] = data;
+}
+
+u8 swp20_device::pan_l_r()
+{
+ return m_pan_l[m_voice];
+}
+
+void swp20_device::pan_r_w(u8 data)
+{
+ m_stream->update();
+ m_pan_r[m_voice] = data;
}
-// init mu80:
-// 48394: <- 47aea
-// write 04.7f 00.14 01.90 to +3c
-// write 01.90 80-ff, 473ea++
-// write 01.94 80-ff, 4746a++
-// write 01.98 80-ff, 474ea++
-// write 01.9c 80-ff, 4756a++
+u8 swp20_device::pan_r_r()
+{
+ return m_pan_r[m_voice];
+}
-// write 01.a0
-// write 40-5f.data
-// etc
+template<int sel> void swp20_device::pitch_w(u8 data)
+{
+ m_stream->update();
+ m_pitch[m_voice] = (m_pitch[m_voice] & ~(0xff << (8*sel))) | (data << (8*sel));
+}
-void swp20_device::p3c_w(u8 data)
+template<int sel> u8 swp20_device::pitch_r()
{
- if(m_p3c_address)
- m_p3c_port = data;
- else
- logerror("p3c %02x = %02x\n", m_p3c_port, data);
+ return m_pitch[m_voice] >> (8*sel);
+}
+template<int sel> void swp20_device::sample_start_w(u8 data)
+{
+ m_stream->update();
- m_p3c_address = !m_p3c_address;
+ m_sample_start[m_voice] = (m_sample_start[m_voice] & ~(0xff << (8*sel))) | (data << (8*sel));
+ // if(!sel)
+ // logerror("sample_start[%02x] = %04x\n", m_voice, m_sample_start[m_voice]);
+}
+
+template<int sel> u8 swp20_device::sample_start_r()
+{
+ return m_sample_start[m_voice] >> (8*sel);
+}
+
+template<int sel> void swp20_device::sample_end_w(u8 data)
+{
+ m_stream->update();
+
+ m_sample_end[m_voice] = (m_sample_end[m_voice] & ~(0xff << (8*sel))) | (data << (8*sel));
+ // if(!sel)
+ // logerror("sample_end[%02x] = %04x\n", m_voice, m_sample_end[m_voice]);
+}
+
+template<int sel> u8 swp20_device::sample_end_r()
+{
+ return m_sample_end[m_voice] >> (8*sel);
+}
+
+void swp20_device::sample_format_w(u8 data)
+{
+ m_stream->update();
+
+ m_sample_format[m_voice] = data;
+}
+
+u8 swp20_device::sample_format_r()
+{
+ return m_sample_format[m_voice];
+}
+
+template<int sel> void swp20_device::sample_address_w(u8 data)
+{
+ m_stream->update();
+
+ m_sample_address[m_voice] = (m_sample_address[m_voice] & ~(0xff << (8*sel))) | (data << (8*sel));
+ if(!sel)
+ logerror("sample_address[%02x] = %04x\n", m_voice, m_sample_address[m_voice]);
+}
+
+template<int sel> u8 swp20_device::sample_address_r()
+{
+ return m_sample_address[m_voice] >> (8*sel);
+}
+
+void swp20_device::eq_w(u8 data)
+{
+ if(m_eq_address)
+ m_eq_port = data;
+ else {
+ if(0)
+ logerror("eq %02x = %02x\n", m_eq_port, data);
+ }
+
+ m_eq_address = !m_eq_address;
}
u8 swp20_device::snd_r(offs_t offset)
{
- logerror("r %02x %s\n", offset, machine().describe_context());
+ // logerror("r %02x %s\n", offset, machine().describe_context());
return 0;
}
+static u8 rr[0x20*0x40];
+
void swp20_device::snd_w(offs_t offset, u8 data)
{
// Registers 0-f are global, 10-3f per-voice
switch(offset) {
- case 0x01:
- m_voice = data & 0x1f;
- break;
-
case 0x04: case 0x05: case 0x06: case 0x07: {
int off = 8*(offset & 3);
u32 mask = 0xff << off;
@@ -90,19 +210,19 @@ void swp20_device::snd_w(offs_t offset, u8 data)
break;
}
-
- case 0x10: // freq high
- break;
- case 0x11: // freq low
- break;
-
- default:
- logerror("w %02x.%02x, %02x %s\n", m_voice, offset, data, machine().describe_context());
+ default: {
+ if(data != rr[m_voice * 0x40 + offset]) {
+ rr[m_voice * 0x40 + offset] = data;
+ if(offset != 0x34)
+ logerror("w %02x.%02x, %02x %s\n", m_voice, offset, data, machine().describe_context());
+ }
+ }
}
}
void swp20_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
{
outputs[0].fill(0);
+ outputs[1].fill(0);
}
diff --git a/src/devices/sound/swp20.h b/src/devices/sound/swp20.h
index af6bac38bc5..9c2eeafefd6 100644
--- a/src/devices/sound/swp20.h
+++ b/src/devices/sound/swp20.h
@@ -21,17 +21,49 @@ protected:
virtual void device_start() override;
virtual void device_reset() override;
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
- virtual void rom_bank_updated() override;
private:
- u8 m_p3c_port;
- bool m_p3c_address;
+ sound_stream *m_stream;
+
+ std::array<u8, 0x20> m_pan_l;
+ std::array<u8, 0x20> m_pan_r;
+ std::array<u16, 0x20> m_pitch;
+ std::array<u32, 0x20> m_sample_start;
+ std::array<u16, 0x20> m_sample_end;
+ std::array<u8, 0x20> m_sample_format;
+ std::array<u32, 0x20> m_sample_address;
+
+ u16 m_waverom_val;
+ u8 m_waverom_access;
+
+ u8 m_eq_port;
+ bool m_eq_address;
u8 m_voice;
u32 m_keyon;
u32 m_keyoff;
- // Generic upload port
- void p3c_w(u8 data);
+ void voice_w(u8 data);
+
+ void pan_l_w(u8 data);
+ u8 pan_l_r();
+ void pan_r_w(u8 data);
+ u8 pan_r_r();
+ template<int sel> void pitch_w(u8 data);
+ template<int sel> u8 pitch_r();
+ template<int sel> void sample_start_w(u8 data);
+ template<int sel> u8 sample_start_r();
+ template<int sel> void sample_end_w(u8 data);
+ template<int sel> u8 sample_end_r();
+ void sample_format_w(u8 data);
+ u8 sample_format_r();
+ template<int sel> void sample_address_w(u8 data);
+ template<int sel> u8 sample_address_r();
+
+ void waverom_access_w(u8 data);
+ template<int sel> u8 waverom_val_r();
+
+ // Generic upload port, connected to the EQ on the first swp20
+ void eq_w(u8 data);
// Generic catch-all
u8 snd_r(offs_t offset);
diff --git a/src/devices/sound/swp30.cpp b/src/devices/sound/swp30.cpp
index f133f21dc5e..80b123aba5a 100644
--- a/src/devices/sound/swp30.cpp
+++ b/src/devices/sound/swp30.cpp
@@ -4,6 +4,7 @@
// Yamaha SWP30/30B, rompler/dsp combo
#include "emu.h"
+#include "debugger.h"
#include "swp30.h"
static int scount = 0;
@@ -33,9 +34,9 @@ static int scount = 0;
per-channel values for AWM2, but parts are isolated and renumbered
for MEG regisrers or for general control functions.
- Names we'll use in th rest of the text:
+ Names we'll use in the rest of the text:
- reg(y, x) is the register at address 2*(y*0x40 + x)
- - ch<nn> is reg(channel, xx) for a given AWG2 channel
+ - ch<nn> is reg(channel, nn) for a given AWG2 channel
- sy<nn> is reg(nn/2, 0xe + (nn % 2))
- fp<nnn> is reg(nn/6, 0x21 + 2*(nn % 6))
- of<nn> is reg(nn/2, 0x30 + (nn % 2))
@@ -64,10 +65,10 @@ static int scount = 0;
ch03 40ff at startup, 5010 always afterwards?
ch04 fixed LPF resonance level
ch05 unknown
- ch06 attack, bit 14-8 = step, bit 7 = skip
- ch07 decay, bit 14-8 = step, bit 7-0 = target attenuation (top 8 bits)
- ch08 release, bit 14-8 = step, bit 7-0 = target attenuation (top 8 bits)
- ch09 base volume bit 15 = activate release, bit 14-8 unknown, bit 7-0 = initial attenuation
+ ch06 attack, bit 14-8 = step, bit 7 = mode
+ ch07 decay1, bit 14-8 = step, bit 7-0 = target attenuation (top 8 bits)
+ ch08 decay2, bit 14-8 = step, bit 7-0 = target attenuation (top 8 bits)
+ ch09 base volume bit 15 = activate decay2, bit 14-8 unknown, bit 7-0 = initial attenuation
ch0a-0d unknown, probably something to do with pitch eg
ch10 unknown
@@ -98,13 +99,6 @@ static int scount = 0;
giving a value between 8 and 0x78000. This value is added or
substracted after each sample.
- For attack the actual range of steps is 8..119, giving an increment
- of 0x10 to 0x3c000, and a full sweep from -96dB to 0 in 95s (8) to
- 6.2ms (119).
-
- For decay and release the range is 1..120, e.g. 9 to 0x40000, or
- 169s to 5.8ms for a full sweep.
-
MEG:
@@ -123,7 +117,7 @@ static int scount = 0;
Every 44100th of a second the 384 program steps are run once in
order (no branches) to compute everything.
- 24 LFO registers are available (possibly more). The LFO registers
+ 24 LFO registers are available. The LFO registers
internal counters are 22 bits wide. The LSB of the register gives
the increment per sample, encoded in a special 3.5 format.
With scale = 3bits and v = 5bits,
@@ -132,14 +126,16 @@ static int scount = 0;
shift = { 0, 0, 1, 2, 3, 4, 5, 6 }
The 21th bit of the counter inverts bits 20-0 on read, those are
- interpreted as a 0-1 value, giving a sawtooth wave.
+ interpreted as a 0-1 value, giving a sawtooth wave. When an
+ instruction uses the lfo, which one is selected by using pc/16.
8 mappings can be setup, which allow to manage rotating buffers in
the samples ram easily by automating masking and offset adding. The
- register format is: tttttsss oooooooo. 't' is not understood
- yet. 's' is the sub-buffer size, defined as 1 << (10+s). The base
- offset is o << 10. There are no alignment issues, e.g. you can have
- a buffer at 0x28000 which is 0x10000 samples long.
+ register format is: pppppsss oooooooo. 'p' is the base pc/12 at
+ which the map starts to be used. 's' is the sub-buffer size,
+ defined as 1 << (10+s). The base offset is o << 10. There are no
+ alignment issues, e.g. you can have a buffer at 0x28000 which is
+ 0x10000 samples long.
fp<nnn> fixed point 2.14 value associated with instruction nnn
@@ -165,147 +161,437 @@ static int scount = 0;
ch35-37 routing for an AWM2 channel
-
*/
DEFINE_DEVICE_TYPE(SWP30, swp30_device, "swp30", "Yamaha SWP30 sound chip")
-swp30_device::swp30_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, SWP30, tag, owner, clock),
- device_sound_interface(mconfig, *this),
- device_rom_interface(mconfig, *this),
- m_meg(*this, "meg")
+bool swp30_device::istep(s32 &value, s32 limit, s32 step)
{
- (void)m_map;
+ // fprintf(stderr, "istep(%x, %x, %x)\n", value, limit, step);
+ if(value < limit) {
+ value += step;
+ if(value >= limit) {
+ value = limit;
+ return true;
+ }
+ return false;
+ }
+
+ if(value > limit) {
+ value -= step;
+ if(value <= limit) {
+ value = limit;
+ return true;
+ }
+ return false;
+ }
+
+ return true;
}
-void swp30_device::device_add_mconfig(machine_config &config)
+s32 swp30_device::fpadd(s32 value, s32 step)
{
- MEGEMB(config, m_meg);
+ s32 e = value >> 24;
+ s32 m = value & 0xffffff;
+
+ m += step << e;
+ if(m & 0xfe000000)
+ return 0xfffffff;
+
+ while(m & 0x01000000) {
+ m <<= 1;
+ e ++;
+ }
+ if(e >= 16)
+ return 0xfffffff;
+ return (e << 24) | (m & 0xffffff);
}
-void swp30_device::device_start()
+s32 swp30_device::fpsub(s32 value, s32 step)
{
- m_stream = stream_alloc(0, 2, 44100, STREAM_SYNCHRONOUS);
+ s32 e = value >> 24;
+ s32 m = (value & 0xffffff) | 0xfe000000;
+ m = e < 0xc ? m - (step << e) : (m >> (e - 0xb)) - (step << 0xb);
+ if(m >= 0)
+ return 0;
+ if(e >= 0xc)
+ e = 0xb;
+ while(m < 0xfe000000) {
+ if(!e)
+ return 0;
+ e --;
+ m >>= 1;
+ }
+ while(e != 0xf && (m >= 0xff000000)) {
+ e ++;
+ m <<= 1;
+ }
- // Attenuation for panning is 4.4 floating point. That means 0
- // to -96.3dB. Since it's a nice range, we assume it's the same
- // for other attenuation values. Computed value is 1.16
- // format, to avoid overflow
+ return (e << 24) | (m & 0xffffff);
+}
- for(int i=0; i<256; i++)
- m_linear_attenuation[i] = ((32 - (i & 15)) << (0xf ^ (i >> 4))) >> 4;
+bool swp30_device::fpstep(s32 &value, s32 limit, s32 step)
+{
+ // value, limit and step are 4.24 but step has its exponent and
+ // top four bits zero
- // Relative playback frequency of a sample is encoded on signed 14
- // bits. The scale is logarithmic, with 0x400 = 1 octave (e.g. *2
- // or /2).
+ if(value == limit)
+ return true;
+ if(value < limit) {
+ value = fpadd(value, step);
+ if(value >= limit) {
+ value = limit;
+ return true;
+ }
+ return false;
+ }
- for(int i=-0x2000; i<0x2000; i++)
- m_sample_increment[i & 0x3fff] = 256 * pow(2, i/1024.0);
+ value = fpsub(value, step);
+ if(value <= limit) {
+ value = limit;
+ return true;
+ }
+ return false;
+}
- // Log to linear 8-bits sample decompression. Statistics say
- // that's what it should look like. Note that 0 can be encoded
- // both as 0x00 and 0x80, and as it happens 0x80 is never used in
- // these samples. Ends up with a 55dB dynamic range, to compare
- // with 8bits 48dB, 12bits 72dB and 16bits 96dB.
+// sample is signed 24.8
+s32 swp30_device::fpapply(s32 value, s32 sample)
+{
+ if(value >= 0x10000000)
+ return 0;
+ return (s64(sample) - ((s64(sample) * ((value >> 9) & 0x7fff)) >> 16)) >> (value >> 24);
+}
+
+// sample is signed 24.8
+s32 swp30_device::lpffpapply(s32 value, s32 sample)
+{
+ return ((((value >> 7) & 0x7fff) | 0x8000) * s64(sample)) >> (31 - (value >> 22));
+}
+
+// Some tables we picked up from the swp00. May be different, may not be.
+
+const std::array<s32, 0x80> swp30_device::attack_linear_step = {
+ 0x00027, 0x0002b, 0x0002f, 0x00033, 0x00037, 0x0003d, 0x00042, 0x00048,
+ 0x0004d, 0x00056, 0x0005e, 0x00066, 0x0006f, 0x0007a, 0x00085, 0x00090,
+ 0x0009b, 0x000ac, 0x000bd, 0x000cc, 0x000de, 0x000f4, 0x00109, 0x00120,
+ 0x00135, 0x00158, 0x00179, 0x00199, 0x001bc, 0x001e7, 0x00214, 0x00240,
+ 0x0026b, 0x002af, 0x002f2, 0x00332, 0x00377, 0x003d0, 0x0042c, 0x00480,
+ 0x004dc, 0x0055e, 0x005e9, 0x0066e, 0x006f4, 0x007a4, 0x00857, 0x0090b,
+ 0x009c3, 0x00acb, 0x00bd6, 0x00ce6, 0x00e00, 0x00f5e, 0x010d2, 0x01234,
+ 0x0139e, 0x015d0, 0x017f3, 0x01a20, 0x01c4a, 0x01f52, 0x02232, 0x0250f,
+ 0x027ff, 0x02c72, 0x03109, 0x0338b, 0x039c4, 0x04038, 0x04648, 0x04c84,
+ 0x05262, 0x05c1c, 0x065af, 0x06f5c, 0x07895, 0x0866f, 0x09470, 0x0a19e,
+ 0x0ae4c, 0x0c566, 0x0db8d, 0x0f00f, 0x10625, 0x12937, 0x14954, 0x16c17,
+ 0x1886e, 0x1c71c, 0x20000, 0x239e1, 0x2647c, 0x2aaab, 0x2ecfc, 0x3241f,
+ 0x35e51, 0x3a83b, 0x40000, 0x4325c, 0x47dc1, 0x4c8f9, 0x50505, 0x55555,
+ 0x58160, 0x5d174, 0x60606, 0x62b2e, 0x67b24, 0x6a63c, 0x6d3a0, 0x6eb3e,
+ 0x71c72, 0x73616, 0x75075, 0x76b98, 0x78788, 0x78788, 0x7a44c, 0x7a44c,
+ 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c,
+};
+
+const std::array<s32, 0x20> swp30_device::decay_linear_step = {
+ 0x15083, 0x17ad2, 0x1a41a, 0x1cbe7, 0x1f16d, 0x22ef1, 0x26a44, 0x2a1e4,
+ 0x2da35, 0x34034, 0x3a197, 0x40000, 0x45b82, 0x4b809, 0x51833, 0x57262,
+ 0x5d9f7, 0x6483f, 0x6b15c, 0x71c72, 0x77976, 0x7d119, 0x83127, 0x88889,
+ 0x8d3dd, 0x939a8, 0x991f2, 0x9d89e, 0xa0a0a, 0xa57eb, 0xa72f0, 0xac769,
+};
+
+// Pitch conversion table, 2**(31 + i/0x400)
+const std::array<u32, 0x400> swp30_device::pitch_base = {
+ 0x80000000, 0x8016302f, 0x802c6436, 0x80429c17, 0x8058d7d2, 0x806f1768, 0x80855ad9, 0x809ba226,
+ 0x80b1ed4f, 0x80c83c56, 0x80de8f3b, 0x80f4e5ff, 0x810b40a1, 0x81219f24, 0x81380188, 0x814e67cc,
+ 0x8164d1f3, 0x817b3ffd, 0x8191b1ea, 0x81a827ba, 0x81bea170, 0x81d51f0b, 0x81eba08c, 0x820225f4,
+ 0x8218af43, 0x822f3c7a, 0x8245cd9a, 0x825c62a4, 0x8272fb97, 0x82899876, 0x82a0393f, 0x82b6ddf5,
+ 0x82cd8698, 0x82e43329, 0x82fae3a7, 0x83119814, 0x83285071, 0x833f0cbf, 0x8355ccfd, 0x836c912c,
+ 0x8383594e, 0x839a2563, 0x83b0f56c, 0x83c7c969, 0x83dea15b, 0x83f57d43, 0x840c5d21, 0x842340f6,
+ 0x843a28c3, 0x84511489, 0x84680447, 0x847ef800, 0x8495efb3, 0x84aceb61, 0x84c3eb0b, 0x84daeeb2,
+ 0x84f1f656, 0x850901f8, 0x85201198, 0x85372538, 0x854e3cd8, 0x85655879, 0x857c781c, 0x85939bc0,
+ 0x85aac367, 0x85c1ef12, 0x85d91ec1, 0x85f05275, 0x86078a2f, 0x861ec5ef, 0x863605b5, 0x864d4984,
+ 0x8664915b, 0x867bdd3b, 0x86932d25, 0x86aa811a, 0x86c1d919, 0x86d93525, 0x86f0953d, 0x8707f963,
+ 0x871f6196, 0x8736cdd8, 0x874e3e2a, 0x8765b28c, 0x877d2afe, 0x8794a783, 0x87ac2819, 0x87c3acc2,
+ 0x87db357f, 0x87f2c251, 0x880a5337, 0x8821e834, 0x88398146, 0x88511e70, 0x8868bfb2, 0x8880650c,
+ 0x88980e80, 0x88afbc0e, 0x88c76db6, 0x88df237a, 0x88f6dd5a, 0x890e9b57, 0x89265d72, 0x893e23ab,
+ 0x8955ee03, 0x896dbc7b, 0x89858f13, 0x899d65cc, 0x89b540a7, 0x89cd1fa5, 0x89e502c6, 0x89fcea0b,
+ 0x8a14d575, 0x8a2cc504, 0x8a44b8ba, 0x8a5cb096, 0x8a74ac9a, 0x8a8cacc6, 0x8aa4b11c, 0x8abcb99b,
+ 0x8ad4c645, 0x8aecd71a, 0x8b04ec1b, 0x8b1d0548, 0x8b3522a3, 0x8b4d442c, 0x8b6569e4, 0x8b7d93cc,
+ 0x8b95c1e3, 0x8badf42c, 0x8bc62aa7, 0x8bde6554, 0x8bf6a434, 0x8c0ee748, 0x8c272e91, 0x8c3f7a10,
+ 0x8c57c9c4, 0x8c701daf, 0x8c8875d2, 0x8ca0d22d, 0x8cb932c1, 0x8cd1978f, 0x8cea0098, 0x8d026ddb,
+ 0x8d1adf5b, 0x8d335517, 0x8d4bcf11, 0x8d644d49, 0x8d7ccfc0, 0x8d955677, 0x8dade16e, 0x8dc670a6,
+ 0x8ddf0420, 0x8df79bdc, 0x8e1037dc, 0x8e28d820, 0x8e417ca9, 0x8e5a2577, 0x8e72d28c, 0x8e8b83e7,
+ 0x8ea4398b, 0x8ebcf377, 0x8ed5b1ac, 0x8eee742b, 0x8f073af5, 0x8f20060b, 0x8f38d56c, 0x8f51a91b,
+ 0x8f6a8117, 0x8f835d62, 0x8f9c3dfc, 0x8fb522e6, 0x8fce0c21, 0x8fe6f9ae, 0x8fffeb8c, 0x9018e1bd,
+ 0x9031dc43, 0x904adb1c, 0x9063de4b, 0x907ce5d0, 0x9095f1ab, 0x90af01de, 0x90c81669, 0x90e12f4e,
+ 0x90fa4c8b, 0x91136e24, 0x912c9417, 0x9145be67, 0x915eed13, 0x9178201d, 0x91915785, 0x91aa934c,
+ 0x91c3d373, 0x91dd17fb, 0x91f660e3, 0x920fae2e, 0x9228ffdc, 0x924255ed, 0x925bb062, 0x92750f3d,
+ 0x928e727d, 0x92a7da24, 0x92c14632, 0x92dab6a9, 0x92f42b88, 0x930da4d2, 0x93272285, 0x9340a4a4,
+ 0x935a2b2f, 0x9373b626, 0x938d458b, 0x93a6d95e, 0x93c071a0, 0x93da0e52, 0x93f3af75, 0x940d5509,
+ 0x9426ff0f, 0x9440ad88, 0x945a6075, 0x947417d6, 0x948dd3ac, 0x94a793f8, 0x94c158bb, 0x94db21f6,
+ 0x94f4efa8, 0x950ec1d4, 0x9528987a, 0x9542739a, 0x955c5336, 0x9576374e, 0x95901fe3, 0x95aa0cf5,
+ 0x95c3fe86, 0x95ddf497, 0x95f7ef27, 0x9611ee38, 0x962bf1cb, 0x9645f9e1, 0x96600679, 0x967a1795,
+ 0x96942d37, 0x96ae475d, 0x96c8660a, 0x96e2893f, 0x96fcb0fb, 0x9716dd3f, 0x97310e0e, 0x974b4366,
+ 0x97657d49, 0x977fbbb9, 0x9799feb5, 0x97b4463e, 0x97ce9255, 0x97e8e2fc, 0x98033832, 0x981d91f9,
+ 0x9837f051, 0x9852533b, 0x986cbab9, 0x988726c9, 0x98a1976f, 0x98bc0caa, 0x98d6867b, 0x98f104e2,
+ 0x990b87e2, 0x99260f7a, 0x99409bab, 0x995b2c77, 0x9975c1dd, 0x99905bdf, 0x99aafa7d, 0x99c59db9,
+ 0x99e04593, 0x99faf20b, 0x9a15a324, 0x9a3058dc, 0x9a4b1337, 0x9a65d233, 0x9a8095d2, 0x9a9b5e15,
+ 0x9ab62afc, 0x9ad0fc89, 0x9aebd2bb, 0x9b06ad95, 0x9b218d16, 0x9b3c7140, 0x9b575a14, 0x9b724791,
+ 0x9b8d39b9, 0x9ba8308d, 0x9bc32c0e, 0x9bde2c3c, 0x9bf93118, 0x9c143aa4, 0x9c2f48df, 0x9c4a5bcb,
+ 0x9c657368, 0x9c808fb7, 0x9c9bb0ba, 0x9cb6d670, 0x9cd200db, 0x9ced2ffc, 0x9d0863d3, 0x9d239c61,
+ 0x9d3ed9a7, 0x9d5a1ba6, 0x9d75625e, 0x9d90add1, 0x9dabfdff, 0x9dc752e9, 0x9de2ac90, 0x9dfe0af5,
+ 0x9e196e18, 0x9e34d5fb, 0x9e50429e, 0x9e6bb401, 0x9e872a27, 0x9ea2a50f, 0x9ebe24bb, 0x9ed9a92b,
+ 0x9ef53260, 0x9f10c05b, 0x9f2c531d, 0x9f47eaa6, 0x9f6386f8, 0x9f7f2814, 0x9f9acdf9, 0x9fb678a9,
+ 0x9fd22825, 0x9feddc6d, 0xa0099583, 0xa0255367, 0xa041161b, 0xa05cdd9e, 0xa078a9f2, 0xa0947b17,
+ 0xa0b0510f, 0xa0cc2bda, 0xa0e80b7a, 0xa103efee, 0xa11fd938, 0xa13bc758, 0xa157ba50, 0xa173b221,
+ 0xa18faeca, 0xa1abb04d, 0xa1c7b6ac, 0xa1e3c1e5, 0xa1ffd1fc, 0xa21be6ef, 0xa23800c1, 0xa2541f72,
+ 0xa2704303, 0xa28c6b74, 0xa2a898c7, 0xa2c4cafc, 0xa2e10215, 0xa2fd3e11, 0xa3197ef3, 0xa335c4ba,
+ 0xa3520f68, 0xa36e5efe, 0xa38ab37c, 0xa3a70ce3, 0xa3c36b34, 0xa3dfce70, 0xa3fc3698, 0xa418a3ac,
+ 0xa43515ae, 0xa4518c9e, 0xa46e087d, 0xa48a894c, 0xa4a70f0c, 0xa4c399be, 0xa4e02962, 0xa4fcbdfa,
+ 0xa5195786, 0xa535f608, 0xa552997f, 0xa56f41ed, 0xa58bef53, 0xa5a8a1b1, 0xa5c55909, 0xa5e2155c,
+ 0xa5fed6a9, 0xa61b9cf3, 0xa6386839, 0xa655387d, 0xa6720dc0, 0xa68ee803, 0xa6abc745, 0xa6c8ab89,
+ 0xa6e594cf, 0xa7028319, 0xa71f7665, 0xa73c6eb7, 0xa7596c0e, 0xa7766e6c, 0xa79375d1, 0xa7b0823e,
+ 0xa7cd93b4, 0xa7eaaa35, 0xa807c5c0, 0xa824e656, 0xa8420bfa, 0xa85f36aa, 0xa87c6669, 0xa8999b38,
+ 0xa8b6d516, 0xa8d41405, 0xa8f15806, 0xa90ea11a, 0xa92bef41, 0xa949427d, 0xa9669ace, 0xa983f836,
+ 0xa9a15ab4, 0xa9bec24b, 0xa9dc2efa, 0xa9f9a0c3, 0xaa1717a7, 0xaa3493a7, 0xaa5214c2, 0xaa6f9afb,
+ 0xaa8d2652, 0xaaaab6c9, 0xaac84c5f, 0xaae5e716, 0xab0386ef, 0xab212bea, 0xab3ed609, 0xab5c854d,
+ 0xab7a39b5, 0xab97f344, 0xabb5b1fa, 0xabd375d8, 0xabf13edf, 0xac0f0d0f, 0xac2ce06a, 0xac4ab8f1,
+ 0xac6896a4, 0xac867985, 0xaca46194, 0xacc24ed1, 0xace0413f, 0xacfe38de, 0xad1c35af, 0xad3a37b3,
+ 0xad583eea, 0xad764b55, 0xad945cf7, 0xadb273ce, 0xadd08fdd, 0xadeeb124, 0xae0cd7a4, 0xae2b035e,
+ 0xae493452, 0xae676a83, 0xae85a5f0, 0xaea3e69b, 0xaec22c84, 0xaee077ad, 0xaefec816, 0xaf1d1dc0,
+ 0xaf3b78ad, 0xaf59d8dc, 0xaf783e50, 0xaf96a908, 0xafb51906, 0xafd38e4b, 0xaff208d8, 0xb01088ad,
+ 0xb02f0dcb, 0xb04d9834, 0xb06c27e8, 0xb08abce8, 0xb0a95736, 0xb0c7f6d1, 0xb0e69bbc, 0xb10545f6,
+ 0xb123f581, 0xb142aa5e, 0xb161648e, 0xb1802411, 0xb19ee8e8, 0xb1bdb315, 0xb1dc8299, 0xb1fb5773,
+ 0xb21a31a6, 0xb2391132, 0xb257f618, 0xb276e059, 0xb295cff5, 0xb2b4c4ef, 0xb2d3bf46, 0xb2f2befc,
+ 0xb311c412, 0xb330ce88, 0xb34fde60, 0xb36ef39a, 0xb38e0e38, 0xb3ad2e3a, 0xb3cc53a1, 0xb3eb7e6e,
+ 0xb40aaea2, 0xb429e43e, 0xb4491f43, 0xb4685fb2, 0xb487a58c, 0xb4a6f0d2, 0xb4c64185, 0xb4e597a5,
+ 0xb504f333, 0xb5245432, 0xb543baa0, 0xb5632681, 0xb58297d3, 0xb5a20e99, 0xb5c18ad3, 0xb5e10c82,
+ 0xb60093a8, 0xb6202044, 0xb63fb259, 0xb65f49e7, 0xb67ee6ee, 0xb69e8971, 0xb6be316f, 0xb6dddeea,
+ 0xb6fd91e3, 0xb71d4a5a, 0xb73d0851, 0xb75ccbc9, 0xb77c94c2, 0xb79c633e, 0xb7bc373d, 0xb7dc10c1,
+ 0xb7fbefca, 0xb81bd459, 0xb83bbe70, 0xb85bae0f, 0xb87ba337, 0xb89b9de9, 0xb8bb9e27, 0xb8dba3f0,
+ 0xb8fbaf47, 0xb91bc02b, 0xb93bd69f, 0xb95bf2a2, 0xb97c1437, 0xb99c3b5d, 0xb9bc6816, 0xb9dc9a63,
+ 0xb9fcd245, 0xba1d0fbc, 0xba3d52ca, 0xba5d9b70, 0xba7de9ae, 0xba9e3d86, 0xbabe96f9, 0xbadef607,
+ 0xbaff5ab2, 0xbb1fc4fa, 0xbb4034e0, 0xbb60aa66, 0xbb81258d, 0xbba1a655, 0xbbc22cbf, 0xbbe2b8cd,
+ 0xbc034a7e, 0xbc23e1d6, 0xbc447ed3, 0xbc652178, 0xbc85c9c5, 0xbca677bb, 0xbcc72b5b, 0xbce7e4a7,
+ 0xbd08a39f, 0xbd296844, 0xbd4a3297, 0xbd6b0299, 0xbd8bd84b, 0xbdacb3af, 0xbdcd94c4, 0xbdee7b8c,
+ 0xbe0f6809, 0xbe305a3b, 0xbe515222, 0xbe724fc1, 0xbe935317, 0xbeb45c27, 0xbed56af1, 0xbef67f75,
+ 0xbf1799b6, 0xbf38b9b4, 0xbf59df6f, 0xbf7b0aea, 0xbf9c3c24, 0xbfbd731f, 0xbfdeafdd, 0xbffff25d,
+ 0xc0213aa1, 0xc04288ab, 0xc063dc7a, 0xc0853610, 0xc0a6956e, 0xc0c7fa95, 0xc0e96586, 0xc10ad642,
+ 0xc12c4cca, 0xc14dc91f, 0xc16f4b42, 0xc190d333, 0xc1b260f5, 0xc1d3f488, 0xc1f58ded, 0xc2172d25,
+ 0xc238d231, 0xc25a7d12, 0xc27c2dc8, 0xc29de456, 0xc2bfa0bc, 0xc2e162fc, 0xc3032b15, 0xc324f909,
+ 0xc346ccda, 0xc368a687, 0xc38a8613, 0xc3ac6b7e, 0xc3ce56c9, 0xc3f047f5, 0xc4123f04, 0xc4343bf6,
+ 0xc4563ecc, 0xc4784787, 0xc49a5629, 0xc4bc6ab2, 0xc4de8523, 0xc500a57e, 0xc522cbc3, 0xc544f7f4,
+ 0xc5672a11, 0xc589621b, 0xc5aba014, 0xc5cde3fd, 0xc5f02dd6, 0xc6127da1, 0xc634d35e, 0xc6572f0f,
+ 0xc67990b5, 0xc69bf851, 0xc6be65e3, 0xc6e0d96d, 0xc70352f0, 0xc725d26c, 0xc74857e4, 0xc76ae358,
+ 0xc78d74c8, 0xc7b00c37, 0xc7d2a9a4, 0xc7f54d12, 0xc817f681, 0xc83aa5f2, 0xc85d5b66, 0xc88016de,
+ 0xc8a2d85c, 0xc8c59fe0, 0xc8e86d6c, 0xc90b40ff, 0xc92e1a9d, 0xc950fa45, 0xc973dff8, 0xc996cbb8,
+ 0xc9b9bd86, 0xc9dcb562, 0xc9ffb34f, 0xca22b74c, 0xca45c15a, 0xca68d17c, 0xca8be7b2, 0xcaaf03fd,
+ 0xcad2265e, 0xcaf54ed6, 0xcb187d66, 0xcb3bb20f, 0xcb5eecd3, 0xcb822db2, 0xcba574ae, 0xcbc8c1c7,
+ 0xcbec14fe, 0xcc0f6e56, 0xcc32cdcd, 0xcc563367, 0xcc799f23, 0xcc9d1104, 0xccc08909, 0xcce40734,
+ 0xcd078b86, 0xcd2b1600, 0xcd4ea6a3, 0xcd723d71, 0xcd95da6a, 0xcdb97d8f, 0xcddd26e2, 0xce00d664,
+ 0xce248c15, 0xce4847f6, 0xce6c0a0a, 0xce8fd250, 0xceb3a0ca, 0xced77579, 0xcefb505e, 0xcf1f317a,
+ 0xcf4318cf, 0xcf67065c, 0xcf8afa24, 0xcfaef428, 0xcfd2f468, 0xcff6fae5, 0xd01b07a2, 0xd03f1a9e,
+ 0xd06333da, 0xd0875359, 0xd0ab791b, 0xd0cfa521, 0xd0f3d76c, 0xd1180ffd, 0xd13c4ed6, 0xd16093f7,
+ 0xd184df62, 0xd1a93117, 0xd1cd8918, 0xd1f1e766, 0xd2164c02, 0xd23ab6ec, 0xd25f2827, 0xd2839fb3,
+ 0xd2a81d91, 0xd2cca1c3, 0xd2f12c49, 0xd315bd25, 0xd33a5457, 0xd35ef1e1, 0xd38395c4, 0xd3a84001,
+ 0xd3ccf099, 0xd3f1a78d, 0xd41664df, 0xd43b288e, 0xd45ff29e, 0xd484c30d, 0xd4a999df, 0xd4ce7713,
+ 0xd4f35aab, 0xd51844a8, 0xd53d350c, 0xd5622bd6, 0xd5872909, 0xd5ac2ca5, 0xd5d136ac, 0xd5f6471f,
+ 0xd61b5dfe, 0xd6407b4b, 0xd6659f08, 0xd68ac934, 0xd6aff9d1, 0xd6d530e1, 0xd6fa6e65, 0xd71fb25d,
+ 0xd744fcca, 0xd76a4daf, 0xd78fa50b, 0xd7b502e1, 0xd7da6731, 0xd7ffd1fc, 0xd8254343, 0xd84abb08,
+ 0xd870394c, 0xd895be0f, 0xd8bb4954, 0xd8e0db1b, 0xd9067364, 0xd92c1232, 0xd951b786, 0xd9776360,
+ 0xd99d15c2, 0xd9c2cead, 0xd9e88e21, 0xda0e5421, 0xda3420ad, 0xda59f3c7, 0xda7fcd6f, 0xdaa5ada6,
+ 0xdacb946f, 0xdaf181c9, 0xdb1775b6, 0xdb3d7038, 0xdb63714f, 0xdb8978fd, 0xdbaf8742, 0xdbd59c20,
+ 0xdbfbb797, 0xdc21d9aa, 0xdc480259, 0xdc6e31a6, 0xdc946791, 0xdcbaa41b, 0xdce0e747, 0xdd073114,
+ 0xdd2d8185, 0xdd53d899, 0xdd7a3653, 0xdda09ab4, 0xddc705bc, 0xdded776e, 0xde13efc9, 0xde3a6ecf,
+ 0xde60f482, 0xde8780e2, 0xdeae13f1, 0xded4adb0, 0xdefb4e1f, 0xdf21f541, 0xdf48a316, 0xdf6f579f,
+ 0xdf9612de, 0xdfbcd4d4, 0xdfe39d82, 0xe00a6ce9, 0xe031430a, 0xe0581fe6, 0xe07f037f, 0xe0a5edd6,
+ 0xe0ccdeec, 0xe0f3d6c2, 0xe11ad559, 0xe141dab2, 0xe168e6cf, 0xe18ff9b1, 0xe1b71359, 0xe1de33c8,
+ 0xe2055aff, 0xe22c8900, 0xe253bdcc, 0xe27af963, 0xe2a23bc7, 0xe2c984fa, 0xe2f0d4fc, 0xe3182bce,
+ 0xe33f8972, 0xe366ede9, 0xe38e5934, 0xe3b5cb55, 0xe3dd444c, 0xe404c41a, 0xe42c4ac2, 0xe453d843,
+ 0xe47b6ca0, 0xe4a307d9, 0xe4caa9ef, 0xe4f252e5, 0xe51a02ba, 0xe541b971, 0xe5697709, 0xe5913b86,
+ 0xe5b906e7, 0xe5e0d92e, 0xe608b25c, 0xe6309273, 0xe6587973, 0xe680675e, 0xe6a85c34, 0xe6d057f8,
+ 0xe6f85aaa, 0xe720644c, 0xe74874df, 0xe7708c63, 0xe798aada, 0xe7c0d046, 0xe7e8fca8, 0xe8113000,
+ 0xe8396a50, 0xe861ab99, 0xe889f3dd, 0xe8b2431c, 0xe8da9958, 0xe902f692, 0xe92b5acb, 0xe953c605,
+ 0xe97c3840, 0xe9a4b17e, 0xe9cd31c0, 0xe9f5b908, 0xea1e4756, 0xea46dcac, 0xea6f790a, 0xea981c73,
+ 0xeac0c6e7, 0xeae97868, 0xeb1230f7, 0xeb3af095, 0xeb63b743, 0xeb8c8502, 0xebb559d4, 0xebde35ba,
+ 0xec0718b6, 0xec3002c8, 0xec58f3f1, 0xec81ec33, 0xecaaeb8f, 0xecd3f207, 0xecfcff9b, 0xed26144d,
+ 0xed4f301e, 0xed785310, 0xeda17d22, 0xedcaae58, 0xedf3e6b1, 0xee1d2630, 0xee466cd5, 0xee6fbaa2,
+ 0xee990f98, 0xeec26bb7, 0xeeebcf03, 0xef15397b, 0xef3eab20, 0xef6823f5, 0xef91a3fb, 0xefbb2b32,
+ 0xefe4b99b, 0xf00e4f39, 0xf037ec0d, 0xf0619017, 0xf08b3b58, 0xf0b4edd3, 0xf0dea788, 0xf1086879,
+ 0xf13230a7, 0xf15c0013, 0xf185d6be, 0xf1afb4aa, 0xf1d999d8, 0xf2038649, 0xf22d79ff, 0xf25774fa,
+ 0xf281773c, 0xf2ab80c6, 0xf2d5919a, 0xf2ffa9b8, 0xf329c923, 0xf353efda, 0xf37e1de1, 0xf3a85337,
+ 0xf3d28fde, 0xf3fcd3d7, 0xf4271f24, 0xf45171c6, 0xf47bcbbe, 0xf4a62d0d, 0xf4d095b5, 0xf4fb05b7,
+ 0xf5257d15, 0xf54ffbce, 0xf57a81e6, 0xf5a50f5c, 0xf5cfa433, 0xf5fa406c, 0xf624e407, 0xf64f8f07,
+ 0xf67a416c, 0xf6a4fb38, 0xf6cfbc6c, 0xf6fa8509, 0xf7255510, 0xf7502c84, 0xf77b0b65, 0xf7a5f1b4,
+ 0xf7d0df73, 0xf7fbd4a2, 0xf826d145, 0xf851d55a, 0xf87ce0e5, 0xf8a7f3e6, 0xf8d30e5e, 0xf8fe3050,
+ 0xf92959bb, 0xf9548aa1, 0xf97fc305, 0xf9ab02e6, 0xf9d64a46, 0xfa019927, 0xfa2cef8a, 0xfa584d70,
+ 0xfa83b2db, 0xfaaf1fcb, 0xfada9443, 0xfb061042, 0xfb3193cc, 0xfb5d1ee0, 0xfb88b181, 0xfbb44baf,
+ 0xfbdfed6c, 0xfc0b96ba, 0xfc374799, 0xfc63000b, 0xfc8ec011, 0xfcba87ac, 0xfce656de, 0xfd122da9,
+ 0xfd3e0c0c, 0xfd69f20b, 0xfd95dfa6, 0xfdc1d4dd, 0xfdedd1b4, 0xfe19d62b, 0xfe45e243, 0xfe71f5fd,
+ 0xfe9e115c, 0xfeca3460, 0xfef65f0a, 0xff22915d, 0xff4ecb59, 0xff7b0cff, 0xffa75652, 0xffd3a751,
+};
+
+// Actual shape of the lfos unknown, since the hardware accepts 4 and
+// 3 are in use (0, 1 and 3) and no recording are currently available
+
+const std::array<u32, 4> swp30_device::lfo_shape_centered_saw = { 0x00000000, 0x00000000, 0xfff00000, 0xfff00000 }; // --////--
+const std::array<u32, 4> swp30_device::lfo_shape_centered_tri = { 0x00000000, 0x0007ffff, 0xfff7ffff, 0xfff00000 }; // --/\/\--
+const std::array<u32, 4> swp30_device::lfo_shape_offset_saw = { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; // __////__
+const std::array<u32, 4> swp30_device::lfo_shape_offset_tri = { 0x00000000, 0x00000000, 0x000fffff, 0x000fffff }; // __/\/\__
- // Rescale so that it's roughly 16 bits. Range ends up being +/- 78c0.
+swp30_device::swp30_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : cpu_device(mconfig, SWP30, tag, owner, clock),
+ device_sound_interface(mconfig, *this),
+ m_program_config("meg_program", ENDIANNESS_LITTLE, 64, 9, -3, address_map_constructor(FUNC(swp30_device::meg_prg_map), this)),
+ m_rom_config("sample_rom", ENDIANNESS_LITTLE, 32, 25, -2),
+ m_reverb_config("reverb_ram", ENDIANNESS_LITTLE, 16, 18, -1, address_map_constructor(FUNC(swp30_device::meg_reverb_map), this))
+{
+}
+
+void swp30_device::device_start()
+{
+ m_program = &space(AS_PROGRAM);
+ m_rom = &space(AS_DATA);
+ m_reverb = &space(AS_REVERB);
+ m_rom->cache(m_rom_cache);
+ m_reverb->cache(m_reverb_cache);
+
+ state_add(STATE_GENPC, "GENPC", m_meg_pc).noshow();
+ state_add(STATE_GENPCBASE, "CURPC", m_meg_pc).noshow();
+ state_add(0, "PC", m_meg_pc);
- for(int i=0; i<32; i++) {
- m_sample_log8[ i] = i << 0;
- m_sample_log8[0x20|i] = (i << 1) + 0x21;
- m_sample_log8[0x40|i] = (i << 2) + 0x62;
- m_sample_log8[0x60|i] = (i << 3) + 0xe3;
+ set_icountptr(m_icount);
+
+ m_stream = stream_alloc(0, 2, 44100, STREAM_SYNCHRONOUS);
+
+ for(int i=0; i != 128; i++) {
+ u32 v = 0;
+ switch(i >> 3) {
+ default: v = ((i & 7) + 8) << (1 + (i >> 3)); break;
+ case 0xb: v = ((i & 7) + 4) << 13; break;
+ case 0xc: v = ((i & 6) + 6) << 14; break;
+ case 0xd: v = ((i & 4) + 7) << 15; break;
+ case 0xe: v = 15 << 15; break;
+ case 0xf: v = 31 << 15; break;
+ }
+ m_global_step[i] = v;
}
+
+ // Delta-packed samples decompression.
+
for(int i=0; i<128; i++) {
- m_sample_log8[i] = m_sample_log8[i] << 6;
- m_sample_log8[i | 0x80] = -m_sample_log8[i];
+ s16 base = ((i & 0x1f) << (5+(i >> 5))) + (((1 << (i >> 5))-1) << 10);
+ m_dpcm[i | 0x80] = - base;
+ m_dpcm[i] = + base;
}
- save_item(NAME(m_program));
-
save_item(NAME(m_keyon_mask));
- save_item(NAME(m_pre_size));
- save_item(NAME(m_post_size));
- save_item(NAME(m_address));
+ save_item(NAME(m_sample_start));
+ save_item(NAME(m_sample_end));
+ save_item(NAME(m_sample_address));
+ save_item(NAME(m_pitch));
- save_item(NAME(m_sample_pos));
- save_item(NAME(m_sample_history));
+ save_item(NAME(m_release_glo));
- save_item(NAME(m_program_pfp));
- save_item(NAME(m_program_pint));
- save_item(NAME(m_program_plfo));
+ save_item(NAME(m_lfo_step_pmod));
+ save_item(NAME(m_lfo_amod));
- save_item(NAME(m_base_volume));
- save_item(NAME(m_current_volume));
- save_item(NAME(m_mode));
- save_item(NAME(m_freq));
- save_item(NAME(m_pan));
save_item(NAME(m_attack));
- save_item(NAME(m_decay));
- save_item(NAME(m_release));
+ save_item(NAME(m_decay1));
+ save_item(NAME(m_decay2));
+
+ save_item(NAME(m_lfo_phase));
+ save_item(NAME(m_sample_pos));
+ save_item(NAME(m_envelope_level));
+ save_item(NAME(m_envelope_on_timer));
+ save_item(NAME(m_envelope_timer));
+ save_item(NAME(m_decay2_done));
+ save_item(NAME(m_envelope_mode));
+ save_item(NAME(m_glo_level_cur));
+
+ save_item(NAME(m_dpcm_current));
+ save_item(NAME(m_dpcm_next));
+ save_item(NAME(m_dpcm_address));
+
+ save_item(NAME(m_sample_history));
+
save_item(NAME(m_lpf_cutoff));
save_item(NAME(m_lpf_cutoff_inc));
save_item(NAME(m_lpf_reso));
save_item(NAME(m_hpf_cutoff));
save_item(NAME(m_eq_filter));
- save_item(NAME(m_routing));
save_item(NAME(m_internal_adr));
- save_item(NAME(m_program_address));
+ save_item(NAME(m_meg_program_address));
save_item(NAME(m_waverom_adr));
save_item(NAME(m_waverom_mode));
save_item(NAME(m_waverom_access));
save_item(NAME(m_waverom_val));
+
+ save_item(NAME(m_meg_program));
+ save_item(NAME(m_meg_const));
+ save_item(NAME(m_meg_offset));
+ save_item(NAME(m_meg_lfo));
+ save_item(NAME(m_meg_map));
+
+ save_item(STRUCT_MEMBER(m_mixer, vol));
+ save_item(STRUCT_MEMBER(m_mixer, route));
}
void swp30_device::device_reset()
{
- memset(m_program, 0, sizeof(m_program));
-
m_keyon_mask = 0;
- memset(m_pre_size, 0, sizeof(m_pre_size));
- memset(m_post_size, 0, sizeof(m_post_size));
- memset(m_address, 0, sizeof(m_address));
+ std::fill(m_sample_start.begin(), m_sample_start.end(), 0);
+ std::fill(m_sample_end.begin(), m_sample_end.end(), 0);
+ std::fill(m_sample_address.begin(), m_sample_address.end(), 0);
+ std::fill(m_pitch.begin(), m_pitch.end(), 0);
+
+ std::fill(m_release_glo.begin(), m_release_glo.end(), 0);
+
+ std::fill(m_lfo_step_pmod.begin(), m_lfo_step_pmod.end(), 0);
+ std::fill(m_lfo_amod.begin(), m_lfo_amod.end(), 0);
+
+ std::fill(m_attack.begin(), m_attack.end(), 0);
+ std::fill(m_decay1.begin(), m_decay1.end(), 0);
+ std::fill(m_decay2.begin(), m_decay2.end(), 0);
+
+ std::fill(m_lfo_phase.begin(), m_lfo_phase.end(), 0);
+ std::fill(m_sample_pos.begin(), m_sample_pos.end(), 0);
+ std::fill(m_envelope_level.begin(), m_envelope_level.end(), 0);
+ std::fill(m_envelope_timer.begin(), m_envelope_timer.end(), 0);
+ std::fill(m_envelope_on_timer.begin(), m_envelope_on_timer.end(), false);
+ std::fill(m_decay2_done.begin(), m_decay2_done.end(), false);
+ std::fill(m_envelope_mode.begin(), m_envelope_mode.end(), IDLE);
+ std::fill(m_glo_level_cur.begin(), m_glo_level_cur.end(), 0);
+
+ std::fill(m_dpcm_current.begin(), m_dpcm_current.end(), false);
+ std::fill(m_dpcm_next.begin(), m_dpcm_next.end(), false);
+ std::fill(m_dpcm_address.begin(), m_dpcm_address.end(), false);
+
+ std::fill(m_meg_program.begin(), m_meg_program.end(), 0);
+ std::fill(m_meg_const.begin(), m_meg_const.end(), 0);
+ std::fill(m_meg_offset.begin(), m_meg_offset.end(), 0);
+ std::fill(m_meg_lfo.begin(), m_meg_lfo.end(), 0);
+ std::fill(m_meg_map.begin(), m_meg_map.end(), 0);
+
+ std::fill(m_mixer.begin(), m_mixer.end(), mixer_slot());
- memset(m_sample_pos, 0, sizeof(m_sample_pos));
memset(m_sample_history, 0, sizeof(m_sample_history));
- memset(m_program_pfp, 0, sizeof(m_program_pfp));
- memset(m_program_pint, 0, sizeof(m_program_pint));
- memset(m_program_plfo, 0, sizeof(m_program_plfo));
-
- memset(m_base_volume, 0, sizeof(m_base_volume));
- memset(m_current_volume, 0, sizeof(m_current_volume));
- memset(m_mode, IDLE, sizeof(m_mode));
- memset(m_freq, 0, sizeof(m_freq));
- memset(m_pan, 0, sizeof(m_pan));
- memset(m_attack, 0, sizeof(m_attack));
- memset(m_decay, 0, sizeof(m_decay));
- memset(m_release, 0, sizeof(m_release));
memset(m_lpf_cutoff, 0, sizeof(m_lpf_cutoff));
memset(m_lpf_cutoff_inc, 0, sizeof(m_lpf_cutoff_inc));
memset(m_lpf_reso, 0, sizeof(m_lpf_reso));
memset(m_hpf_cutoff, 0, sizeof(m_hpf_cutoff));
memset(m_eq_filter, 0, sizeof(m_eq_filter));
- memset(m_routing, 0, sizeof(m_routing));
- m_program_address = 0;
+ m_meg_program_address = 0;
m_waverom_adr = 0;
m_waverom_mode = 0;
m_waverom_access = 0;
m_waverom_val = 0;
}
-void swp30_device::rom_bank_updated()
-{
- // Nothing to do, stream is synchronous
-}
-
void swp30_device::map(address_map &map)
{
map(0x0000, 0x1fff).rw(FUNC(swp30_device::snd_r), FUNC(swp30_device::snd_w));
@@ -315,20 +601,21 @@ void swp30_device::map(address_map &map)
rchan(map, 0x02).rw(FUNC(swp30_device::hpf_cutoff_r), FUNC(swp30_device::hpf_cutoff_w));
// 03 seems to always get 5010 except at startup where it's 40ff
rchan(map, 0x04).rw(FUNC(swp30_device::lpf_reso_r), FUNC(swp30_device::lpf_reso_w));
- // 05 missing
+ rchan(map, 0x05).rw(FUNC(swp30_device::lfo_amod_r), FUNC(swp30_device::lfo_amod_w));
rchan(map, 0x06).rw(FUNC(swp30_device::attack_r), FUNC(swp30_device::attack_w));
- rchan(map, 0x07).rw(FUNC(swp30_device::decay_r), FUNC(swp30_device::decay_w));
- rchan(map, 0x08).rw(FUNC(swp30_device::release_r), FUNC(swp30_device::release_w));
- rchan(map, 0x09).rw(FUNC(swp30_device::base_volume_r), FUNC(swp30_device::base_volume_w));
- // 0a-0d missing
+ rchan(map, 0x07).rw(FUNC(swp30_device::decay1_r), FUNC(swp30_device::decay1_w));
+ rchan(map, 0x08).rw(FUNC(swp30_device::decay2_r), FUNC(swp30_device::decay2_w));
+ rchan(map, 0x09).rw(FUNC(swp30_device::release_glo_r), FUNC(swp30_device::release_glo_w));
+ rchan(map, 0x0a).rw(FUNC(swp30_device::lfo_step_pmod_r), FUNC(swp30_device::lfo_step_pmod_w));
+ // 0b-0d missing
// 10 missing
- rchan(map, 0x11).rw(FUNC(swp30_device::freq_r), FUNC(swp30_device::freq_w));
- rchan(map, 0x12).rw(FUNC(swp30_device::pre_size_h_r), FUNC(swp30_device::pre_size_h_w));
- rchan(map, 0x13).rw(FUNC(swp30_device::pre_size_l_r), FUNC(swp30_device::pre_size_l_w));
- rchan(map, 0x14).rw(FUNC(swp30_device::post_size_h_r), FUNC(swp30_device::post_size_h_w));
- rchan(map, 0x15).rw(FUNC(swp30_device::post_size_l_r), FUNC(swp30_device::post_size_l_w));
- rchan(map, 0x16).rw(FUNC(swp30_device::address_h_r), FUNC(swp30_device::address_h_w));
- rchan(map, 0x17).rw(FUNC(swp30_device::address_l_r), FUNC(swp30_device::address_l_w));
+ rchan(map, 0x11).rw(FUNC(swp30_device::pitch_r), FUNC(swp30_device::pitch_w));
+ rchan(map, 0x12).rw(FUNC(swp30_device::sample_start_h_r), FUNC(swp30_device::sample_start_h_w));
+ rchan(map, 0x13).rw(FUNC(swp30_device::sample_start_l_r), FUNC(swp30_device::sample_start_l_w));
+ rchan(map, 0x14).rw(FUNC(swp30_device::sample_end_h_r), FUNC(swp30_device::sample_end_h_w));
+ rchan(map, 0x15).rw(FUNC(swp30_device::sample_end_l_r), FUNC(swp30_device::sample_end_l_w));
+ rchan(map, 0x16).rw(FUNC(swp30_device::sample_address_h_r), FUNC(swp30_device::sample_address_h_w));
+ rchan(map, 0x17).rw(FUNC(swp30_device::sample_address_l_r), FUNC(swp30_device::sample_address_l_w));
rchan(map, 0x20).rw(FUNC(swp30_device::eq_filter_r<0>), FUNC(swp30_device::eq_filter_w<0>));
rchan(map, 0x22).rw(FUNC(swp30_device::eq_filter_r<1>), FUNC(swp30_device::eq_filter_w<1>));
rchan(map, 0x24).rw(FUNC(swp30_device::eq_filter_r<2>), FUNC(swp30_device::eq_filter_w<2>));
@@ -336,13 +623,6 @@ void swp30_device::map(address_map &map)
rchan(map, 0x28).rw(FUNC(swp30_device::eq_filter_r<4>), FUNC(swp30_device::eq_filter_w<4>));
rchan(map, 0x2a).rw(FUNC(swp30_device::eq_filter_r<5>), FUNC(swp30_device::eq_filter_w<5>));
// 2c-2f missing
- rchan(map, 0x32).rw(FUNC(swp30_device::pan_r), FUNC(swp30_device::pan_w));
- rchan(map, 0x33).rw(FUNC(swp30_device::dry_rev_r), FUNC(swp30_device::dry_rev_w));
- rchan(map, 0x34).rw(FUNC(swp30_device::cho_var_r), FUNC(swp30_device::cho_var_w));
- rchan(map, 0x35).rw(FUNC(swp30_device::routing_r<0>), FUNC(swp30_device::routing_w<0>));
- rchan(map, 0x36).rw(FUNC(swp30_device::routing_r<1>), FUNC(swp30_device::routing_w<1>));
- rchan(map, 0x37).rw(FUNC(swp30_device::routing_r<2>), FUNC(swp30_device::routing_w<2>));
- // 38-3d missing, are special
// Control registers
// These appear as channel slots 0x0e and 0x0f
@@ -363,32 +643,46 @@ void swp30_device::map(address_map &map)
rctrl(map, 0x0f).rw(FUNC(swp30_device::keyon_mask_r<0>), FUNC(swp30_device::keyon_mask_w<0>));
rctrl(map, 0x10).rw(FUNC(swp30_device::keyon_r), FUNC(swp30_device::keyon_w));
// 11-20 missing
- rctrl(map, 0x21).rw(FUNC(swp30_device::prg_address_r), FUNC(swp30_device::prg_address_w));
- rctrl(map, 0x22).rw(FUNC(swp30_device::prg_r<0>), FUNC(swp30_device::prg_w<0>));
- rctrl(map, 0x23).rw(FUNC(swp30_device::prg_r<1>), FUNC(swp30_device::prg_w<1>));
- rctrl(map, 0x24).rw(FUNC(swp30_device::prg_r<2>), FUNC(swp30_device::prg_w<2>));
- rctrl(map, 0x25).rw(FUNC(swp30_device::prg_r<3>), FUNC(swp30_device::prg_w<3>));
+ rctrl(map, 0x21).rw(FUNC(swp30_device::meg_prg_address_r), FUNC(swp30_device::meg_prg_address_w));
+ rctrl(map, 0x22).rw(FUNC(swp30_device::meg_prg_r<0>), FUNC(swp30_device::meg_prg_w<0>));
+ rctrl(map, 0x23).rw(FUNC(swp30_device::meg_prg_r<1>), FUNC(swp30_device::meg_prg_w<1>));
+ rctrl(map, 0x24).rw(FUNC(swp30_device::meg_prg_r<2>), FUNC(swp30_device::meg_prg_w<2>));
+ rctrl(map, 0x25).rw(FUNC(swp30_device::meg_prg_r<3>), FUNC(swp30_device::meg_prg_w<3>));
// 26-7f missing
- rctrl(map, 0x30).rw(FUNC(swp30_device::map_r<0>), FUNC(swp30_device::map_w<0>));
- rctrl(map, 0x32).rw(FUNC(swp30_device::map_r<1>), FUNC(swp30_device::map_w<1>));
- rctrl(map, 0x34).rw(FUNC(swp30_device::map_r<2>), FUNC(swp30_device::map_w<2>));
- rctrl(map, 0x36).rw(FUNC(swp30_device::map_r<3>), FUNC(swp30_device::map_w<3>));
- rctrl(map, 0x38).rw(FUNC(swp30_device::map_r<4>), FUNC(swp30_device::map_w<4>));
- rctrl(map, 0x3a).rw(FUNC(swp30_device::map_r<5>), FUNC(swp30_device::map_w<5>));
- rctrl(map, 0x3c).rw(FUNC(swp30_device::map_r<6>), FUNC(swp30_device::map_w<6>));
- rctrl(map, 0x3e).rw(FUNC(swp30_device::map_r<7>), FUNC(swp30_device::map_w<7>));
+ rctrl(map, 0x30).rw(FUNC(swp30_device::meg_map_r<0>), FUNC(swp30_device::meg_map_w<0>));
+ rctrl(map, 0x32).rw(FUNC(swp30_device::meg_map_r<1>), FUNC(swp30_device::meg_map_w<1>));
+ rctrl(map, 0x34).rw(FUNC(swp30_device::meg_map_r<2>), FUNC(swp30_device::meg_map_w<2>));
+ rctrl(map, 0x36).rw(FUNC(swp30_device::meg_map_r<3>), FUNC(swp30_device::meg_map_w<3>));
+ rctrl(map, 0x38).rw(FUNC(swp30_device::meg_map_r<4>), FUNC(swp30_device::meg_map_w<4>));
+ rctrl(map, 0x3a).rw(FUNC(swp30_device::meg_map_r<5>), FUNC(swp30_device::meg_map_w<5>));
+ rctrl(map, 0x3c).rw(FUNC(swp30_device::meg_map_r<6>), FUNC(swp30_device::meg_map_w<6>));
+ rctrl(map, 0x3e).rw(FUNC(swp30_device::meg_map_r<7>), FUNC(swp30_device::meg_map_w<7>));
// MEG registers
- rchan(map, 0x21).rw(FUNC(swp30_device::prg_fp_r<0>), FUNC(swp30_device::prg_fp_w<0>));
- rchan(map, 0x23).rw(FUNC(swp30_device::prg_fp_r<1>), FUNC(swp30_device::prg_fp_w<1>));
- rchan(map, 0x25).rw(FUNC(swp30_device::prg_fp_r<2>), FUNC(swp30_device::prg_fp_w<2>));
- rchan(map, 0x27).rw(FUNC(swp30_device::prg_fp_r<3>), FUNC(swp30_device::prg_fp_w<3>));
- rchan(map, 0x29).rw(FUNC(swp30_device::prg_fp_r<4>), FUNC(swp30_device::prg_fp_w<4>));
- rchan(map, 0x2b).rw(FUNC(swp30_device::prg_fp_r<5>), FUNC(swp30_device::prg_fp_w<5>));
- rchan(map, 0x30).rw(FUNC(swp30_device::prg_off_r<0>), FUNC(swp30_device::prg_off_w<0>));
- rchan(map, 0x31).rw(FUNC(swp30_device::prg_off_r<1>), FUNC(swp30_device::prg_off_w<1>));
- rchan(map, 0x3e).rw(FUNC(swp30_device::prg_lfo_r<0>), FUNC(swp30_device::prg_lfo_w<0>));
- rchan(map, 0x3f).rw(FUNC(swp30_device::prg_lfo_r<1>), FUNC(swp30_device::prg_lfo_w<1>));
+ rchan(map, 0x21).rw(FUNC(swp30_device::meg_const_r<0>), FUNC(swp30_device::meg_const_w<0>));
+ rchan(map, 0x23).rw(FUNC(swp30_device::meg_const_r<1>), FUNC(swp30_device::meg_const_w<1>));
+ rchan(map, 0x25).rw(FUNC(swp30_device::meg_const_r<2>), FUNC(swp30_device::meg_const_w<2>));
+ rchan(map, 0x27).rw(FUNC(swp30_device::meg_const_r<3>), FUNC(swp30_device::meg_const_w<3>));
+ rchan(map, 0x29).rw(FUNC(swp30_device::meg_const_r<4>), FUNC(swp30_device::meg_const_w<4>));
+ rchan(map, 0x2b).rw(FUNC(swp30_device::meg_const_r<5>), FUNC(swp30_device::meg_const_w<5>));
+ rchan(map, 0x30).rw(FUNC(swp30_device::meg_offset_r<0>), FUNC(swp30_device::meg_offset_w<0>));
+ rchan(map, 0x31).rw(FUNC(swp30_device::meg_offset_r<1>), FUNC(swp30_device::meg_offset_w<1>));
+ rchan(map, 0x3e).rw(FUNC(swp30_device::meg_lfo_r<0>), FUNC(swp30_device::meg_lfo_w<0>));
+ rchan(map, 0x3f).rw(FUNC(swp30_device::meg_lfo_r<1>), FUNC(swp30_device::meg_lfo_w<1>));
+
+ // Mixer registers
+ rchan(map, 0x32).rw(FUNC(swp30_device::vol_r <0x00|0>), FUNC(swp30_device::vol_w <0x00|0>));
+ rchan(map, 0x33).rw(FUNC(swp30_device::vol_r <0x00|1>), FUNC(swp30_device::vol_w <0x00|1>));
+ rchan(map, 0x34).rw(FUNC(swp30_device::vol_r <0x00|2>), FUNC(swp30_device::vol_w <0x00|2>));
+ rchan(map, 0x35).rw(FUNC(swp30_device::route_r<0x00|0>), FUNC(swp30_device::route_w<0x00|0>));
+ rchan(map, 0x36).rw(FUNC(swp30_device::route_r<0x00|1>), FUNC(swp30_device::route_w<0x00|1>));
+ rchan(map, 0x37).rw(FUNC(swp30_device::route_r<0x00|2>), FUNC(swp30_device::route_w<0x00|2>));
+ rchan(map, 0x38).rw(FUNC(swp30_device::vol_r <0x40|0>), FUNC(swp30_device::vol_w <0x40|0>));
+ rchan(map, 0x39).rw(FUNC(swp30_device::vol_r <0x40|1>), FUNC(swp30_device::vol_w <0x40|1>));
+ rchan(map, 0x3a).rw(FUNC(swp30_device::vol_r <0x40|2>), FUNC(swp30_device::vol_w <0x40|2>));
+ rchan(map, 0x3b).rw(FUNC(swp30_device::route_r<0x40|0>), FUNC(swp30_device::route_w<0x40|0>));
+ rchan(map, 0x3c).rw(FUNC(swp30_device::route_r<0x40|1>), FUNC(swp30_device::route_w<0x40|1>));
+ rchan(map, 0x3d).rw(FUNC(swp30_device::route_r<0x40|2>), FUNC(swp30_device::route_w<0x40|2>));
}
// Control registers
@@ -409,63 +703,88 @@ u16 swp30_device::keyon_r()
void swp30_device::keyon_w(u16)
{
- for(int i=0; i<64; i++) {
- u64 mask = u64(1) << i;
+ for(int chan=0; chan<64; chan++) {
+ u64 mask = u64(1) << chan;
if(m_keyon_mask & mask) {
- m_sample_pos[i] = -s32(m_pre_size[i] << 8);
- m_current_volume[i] = (m_base_volume[i] & 0xff) << (26-8);
- change_mode(i, m_base_volume[i] & 0x8000 ? RELEASE : m_attack[i] & 0x80 ? DECAY : ATTACK);
+ m_sample_pos[chan] = -(m_sample_start[chan] & 0xffffff) << 8;
+ if(m_release_glo[chan] & 0x8000) {
+ m_envelope_level[chan] = 0;
+ m_envelope_on_timer[chan] = false;
+ m_envelope_mode[chan] = RELEASE;
+ } else if(m_attack[chan] & 0x80) {
+ m_envelope_level[chan] = 0x8000000;
+ m_envelope_on_timer[chan] = false;
+ m_envelope_mode[chan] = ATTACK;
+ } else {
+ m_envelope_level[chan] = 0;
+ m_envelope_on_timer[chan] = true;
+ m_envelope_timer[chan] = 0x8000000;
+ m_envelope_mode[chan] = ATTACK;
+ }
+
+ m_decay2_done[chan] = false;
+ m_glo_level_cur[chan] = (m_release_glo[chan] & 0xff) << 4;
+
+ m_dpcm_current[chan] = 0;
+ m_dpcm_next[chan] = 0;
+ s32 dt = m_sample_start[chan] & 0xffffff;
+ if(m_sample_end[chan] & 0x80000000)
+ dt = -dt;
+ m_dpcm_address[chan] = ((m_sample_address[chan] & 0xffffff) << 2) - dt;
+
+ m_lfo_phase[chan] = 0;
if(1)
- logerror("[%08d] keyon %02x %08x %08x %08x vol %04x env %04x %04x %04x pan %04x disp %04x %04x\n", scount, i, m_pre_size[i], m_post_size[i], m_address[i], m_base_volume[i], m_attack[i], m_decay[i], m_release[i], m_pan[i], m_dry_rev[i], m_cho_var[i]);
+ logerror("[%08d] keyon %02x %08x %08x %08x vol %04x env %04x %04x %04x pitch %04x pmod %04x\n", scount, chan, m_sample_start[chan], m_sample_end[chan], m_sample_address[chan], m_release_glo[chan], m_attack[chan], m_decay1[chan], m_decay2[chan], m_pitch[chan], m_lfo_step_pmod[chan]);
}
}
m_keyon_mask = 0;
}
-u16 swp30_device::prg_address_r()
+u16 swp30_device::meg_prg_address_r()
{
- return m_program_address;
+ return m_meg_program_address;
}
-void swp30_device::prg_address_w(u16 data)
+void swp30_device::meg_prg_address_w(u16 data)
{
- m_program_address = data;
- if(m_program_address >= 0x180)
- m_program_address = 0;
+ m_meg_program_address = data;
+ if(m_meg_program_address >= 0x180)
+ m_meg_program_address = 0;
}
-template<int sel> u16 swp30_device::prg_r()
+template<int sel> u16 swp30_device::meg_prg_r()
{
constexpr offs_t shift = 48-16*sel;
- return m_meg->prg_r(m_program_address) >> shift;
+ return m_meg_program[m_meg_program_address] >> shift;
}
-template<int sel> void swp30_device::prg_w(u16 data)
+template<int sel> void swp30_device::meg_prg_w(u16 data)
{
constexpr offs_t shift = 48-16*sel;
constexpr u64 mask = ~(u64(0xffff) << shift);
- m_meg->prg_w(m_program_address, (m_meg->prg_r(m_program_address) & mask) | (u64(data) << shift));
+ m_meg_program[m_meg_program_address] = (m_meg_program[m_meg_program_address] & mask) | (u64(data) << shift);
if(sel == 3) {
if(0)
- logerror("program %03x %016x\n", m_program_address, m_program[m_program_address]);
- m_program_address ++;
- if(m_program_address == 0x180)
- m_program_address = 0;
+ logerror("program %03x %016x\n", m_meg_program_address, m_meg_program[m_meg_program_address]);
+ m_meg_program_address ++;
+ if(m_meg_program_address == 0x180)
+ m_meg_program_address = 0;
}
}
-template<int sel> u16 swp30_device::map_r()
+template<int sel> u16 swp30_device::meg_map_r()
{
- return m_meg->map_r(sel);
+ return m_meg_map[sel];
}
-template<int sel> void swp30_device::map_w(u16 data)
+template<int sel> void swp30_device::meg_map_w(u16 data)
{
- m_meg->map_w(sel, data);
+ m_meg_map[sel] = data;
+ logerror("map %x pc = %03x base = %05x size = %05x\n", sel, 12*(data >> 11), (data & 0xff) << 10, 0x400 << ((data >> 8) & 7));
}
@@ -499,7 +818,7 @@ void swp30_device::waverom_access_w(u16 data)
{
m_waverom_access = data;
if(data == 0x8000) {
- m_waverom_val = read_dword(m_waverom_adr << 2);
+ m_waverom_val = m_rom_cache.read_dword(m_waverom_adr);
logerror("waverom read adr=%08x mode=%08x -> %08x\n", m_waverom_adr, m_waverom_mode, m_waverom_val);
}
}
@@ -584,187 +903,181 @@ template<int coef> void swp30_device::eq_filter_w(offs_t offset, u16 data)
m_eq_filter[offset >> 6][coef] = data;
}
-template<int sel> u16 swp30_device::routing_r(offs_t offset)
+template<int sel> u16 swp30_device::vol_r(offs_t offset)
{
- return m_routing[offset >> 6][sel];
+ return m_mixer[(sel & 0x40) | (offset >> 6)].vol[sel & 3];
}
-template<int sel> void swp30_device::routing_w(offs_t offset, u16 data)
+template<int sel> void swp30_device::vol_w(offs_t offset, u16 data)
{
- m_routing[offset >> 6][sel] = data;
+ m_mixer[(sel & 0x40) | (offset >> 6)].vol[sel & 3] = data;
}
-u16 swp30_device::base_volume_r(offs_t offset)
+template<int sel> u16 swp30_device::route_r(offs_t offset)
{
- return m_base_volume[offset >> 6];
+ return m_mixer[(sel & 0x40) | (offset >> 6)].route[sel & 3];
}
-void swp30_device::base_volume_w(offs_t offset, u16 data)
+template<int sel> void swp30_device::route_w(offs_t offset, u16 data)
{
- u8 chan = offset >> 6;
- if(1 && m_base_volume[chan] != data)
- logerror("snd chan %02x volume %02x %02x\n", chan, data >> 8, data & 0xff);
- m_base_volume[chan] = data;
- if((data & 0x8000) && m_mode[chan] != IDLE && m_mode[chan] != RELEASE)
- change_mode(chan, RELEASE);
+ m_mixer[(sel & 0x40) | (offset >> 6)].route[sel & 3] = data;
}
-
-u16 swp30_device::pan_r(offs_t offset)
+u16 swp30_device::release_glo_r(offs_t offset)
{
- return m_pan[offset >> 6];
+ return m_release_glo[offset >> 6];
}
-void swp30_device::pan_w(offs_t offset, u16 data)
+void swp30_device::release_glo_w(offs_t offset, u16 data)
{
u8 chan = offset >> 6;
- if(0 && m_pan[chan] != data)
- logerror("snd chan %02x pan l %02x r %02x\n", chan, data >> 8, data & 0xff);
- m_pan[chan] = data;
+ if(1 && m_release_glo[chan] != data)
+ logerror("snd chan %02x rel/glo %02x %02x\n", chan, data >> 8, data & 0xff);
+ m_release_glo[chan] = data;
+ if((data & 0x8000) && m_envelope_mode[chan] != IDLE && m_envelope_mode[chan] != RELEASE)
+ m_envelope_mode[chan] = RELEASE;
}
-u16 swp30_device::dry_rev_r(offs_t offset)
+u16 swp30_device::pitch_r(offs_t offset)
{
- return m_dry_rev[offset >> 6];
+ return m_pitch[offset >> 6];
}
-void swp30_device::dry_rev_w(offs_t offset, u16 data)
+void swp30_device::pitch_w(offs_t offset, u16 data)
{
u8 chan = offset >> 6;
- if(0 && m_dry_rev[chan] != data)
- logerror("snd chan %02x dry %02x rev %02x\n", chan, data >> 8, data & 0xff);
- m_dry_rev[chan] = data;
+ // delta is 4*256 per octave, positive means higher freq, e.g 4.10 format.
+ s16 v = data & 0x2000 ? data | 0xc000 : data;
+ if(0 && m_pitch[chan] != data)
+ logerror("snd chan %02x pitch %c%c %d.%03x\n", chan, data & 0x8000 ? '#' : '.', data & 0x4000 ? '#' : '.', v / 1024, (v < 0 ? -v : v) & 0x3ff);
+ m_pitch[chan] = data;
}
-u16 swp30_device::cho_var_r(offs_t offset)
+u16 swp30_device::attack_r(offs_t offset)
{
- return m_cho_var[offset >> 6];
+ return m_attack[offset >> 6];
}
-void swp30_device::cho_var_w(offs_t offset, u16 data)
+void swp30_device::attack_w(offs_t offset, u16 data)
{
- u8 chan = offset >> 6;
- if(0 && m_cho_var[chan] != data)
- logerror("snd chan %02x cho %02x var %02x\n", chan, data >> 8, data & 0xff);
- m_cho_var[chan] = data;
+ if(data != m_attack[offset >> 6])
+ logerror("attack[%02x] = %04x\n", offset >> 6, data);
+ m_attack[offset >> 6] = data;
}
-u16 swp30_device::freq_r(offs_t offset)
+u16 swp30_device::decay1_r(offs_t offset)
{
- return m_freq[offset >> 6];
+ return m_decay1[offset >> 6];
}
-void swp30_device::freq_w(offs_t offset, u16 data)
+void swp30_device::decay1_w(offs_t offset, u16 data)
{
- u8 chan = offset >> 6;
- // delta is 4*256 per octave, positive means higher freq, e.g 4.10 format.
- s16 v = data & 0x2000 ? data | 0xc000 : data;
- if(0 && m_freq[chan] != data)
- logerror("snd chan %02x freq %c%c %d.%03x\n", chan, data & 0x8000 ? '#' : '.', data & 0x4000 ? '#' : '.', v / 1024, (v < 0 ? -v : v) & 0x3ff);
- m_freq[chan] = data;
+ logerror("decay1[%02x] = %04x\n", offset >> 6, data);
+ m_decay1[offset >> 6] = data;
}
-u16 swp30_device::attack_r(offs_t offset)
+u16 swp30_device::decay2_r(offs_t offset)
{
- return m_attack[offset >> 6];
+ return m_decay2[offset >> 6];
}
-void swp30_device::attack_w(offs_t offset, u16 data)
+void swp30_device::decay2_w(offs_t offset, u16 data)
{
- m_attack[offset >> 6] = data;
+ logerror("decay2[%02x] = %04x\n", offset >> 6, data);
+ m_decay2[offset >> 6] = data;
}
-u16 swp30_device::decay_r(offs_t offset)
+u16 swp30_device::lfo_step_pmod_r(offs_t offset)
{
- return m_decay[offset >> 6];
+ return m_lfo_step_pmod[offset >> 6];
}
-void swp30_device::decay_w(offs_t offset, u16 data)
+void swp30_device::lfo_step_pmod_w(offs_t offset, u16 data)
{
- m_decay[offset >> 6] = data;
+ // logerror("lfo_step_pmod[%02x] = %04x\n", offset >> 6, data);
+ m_lfo_step_pmod[offset >> 6] = data;
}
-u16 swp30_device::release_r(offs_t offset)
+u16 swp30_device::lfo_amod_r(offs_t offset)
{
- return m_release[offset >> 6];
+ return m_lfo_amod[offset >> 6];
}
-void swp30_device::release_w(offs_t offset, u16 data)
+void swp30_device::lfo_amod_w(offs_t offset, u16 data)
{
- m_release[offset >> 6] = data;
+ // logerror("lfo_amod[%02x] = %04x\n", offset >> 6, data);
+ m_lfo_amod[offset >> 6] = data;
}
-u16 swp30_device::pre_size_h_r(offs_t offset)
+u16 swp30_device::sample_start_h_r(offs_t offset)
{
- return m_pre_size[offset >> 6] >> 16;
+ return m_sample_start[offset >> 6] >> 16;
}
-u16 swp30_device::pre_size_l_r(offs_t offset)
+u16 swp30_device::sample_start_l_r(offs_t offset)
{
- return m_pre_size[offset >> 6];
+ return m_sample_start[offset >> 6];
}
-void swp30_device::pre_size_h_w(offs_t offset, u16 data)
+void swp30_device::sample_start_h_w(offs_t offset, u16 data)
{
u8 chan = offset >> 6;
- m_pre_size[chan] = (m_pre_size[chan] & 0x0000ffff) | (data << 16);
+ m_sample_start[chan] = (m_sample_start[chan] & 0x0000ffff) | (data << 16);
}
-void swp30_device::pre_size_l_w(offs_t offset, u16 data)
+void swp30_device::sample_start_l_w(offs_t offset, u16 data)
{
u8 chan = offset >> 6;
- m_pre_size[chan] = (m_pre_size[chan] & 0xffff0000) | data;
- if(0)
- logerror("snd chan %02x pre-size %02x %06x\n", chan, m_pre_size[chan] >> 24, m_pre_size[chan] & 0xffffff);
+ m_sample_start[chan] = (m_sample_start[chan] & 0xffff0000) | data;
}
-u16 swp30_device::post_size_h_r(offs_t offset)
+u16 swp30_device::sample_end_h_r(offs_t offset)
{
- return m_post_size[offset >> 6] >> 16;
+ return m_sample_end[offset >> 6] >> 16;
}
-u16 swp30_device::post_size_l_r(offs_t offset)
+u16 swp30_device::sample_end_l_r(offs_t offset)
{
- return m_post_size[offset >> 6];
+ return m_sample_end[offset >> 6];
}
-void swp30_device::post_size_h_w(offs_t offset, u16 data)
+void swp30_device::sample_end_h_w(offs_t offset, u16 data)
{
u8 chan = offset >> 6;
- m_post_size[chan] = (m_post_size[chan] & 0x0000ffff) | (data << 16);
+ m_sample_end[chan] = (m_sample_end[chan] & 0x0000ffff) | (data << 16);
}
-void swp30_device::post_size_l_w(offs_t offset, u16 data)
+void swp30_device::sample_end_l_w(offs_t offset, u16 data)
{
u8 chan = offset >> 6;
- m_post_size[chan] = (m_post_size[chan] & 0xffff0000) | data;
+ m_sample_end[chan] = (m_sample_end[chan] & 0xffff0000) | data;
if(0)
- logerror("snd chan %02x post-size %02x %06x\n", chan, m_post_size[chan] >> 24, m_post_size[chan] & 0xffffff);
+ logerror("snd chan %02x post-size %02x %06x\n", chan, m_sample_end[chan] >> 24, m_sample_end[chan] & 0xffffff);
}
-u16 swp30_device::address_h_r(offs_t offset)
+u16 swp30_device::sample_address_h_r(offs_t offset)
{
- return m_address[offset >> 6] >> 16;
+ return m_sample_address[offset >> 6] >> 16;
}
-u16 swp30_device::address_l_r(offs_t offset)
+u16 swp30_device::sample_address_l_r(offs_t offset)
{
- return m_address[offset >> 6];
+ return m_sample_address[offset >> 6];
}
-void swp30_device::address_h_w(offs_t offset, u16 data)
+void swp30_device::sample_address_h_w(offs_t offset, u16 data)
{
u8 chan = offset >> 6;
- m_address[chan] = (m_address[chan] & 0x0000ffff) | (data << 16);
+ m_sample_address[chan] = (m_sample_address[chan] & 0x0000ffff) | (data << 16);
}
-void swp30_device::address_l_w(offs_t offset, u16 data)
+void swp30_device::sample_address_l_w(offs_t offset, u16 data)
{
u8 chan = offset >> 6;
static const char *const formats[4] = { "l16", "l12", "l8", "x8" };
- m_address[chan] = (m_address[chan] & 0xffff0000) | data;
+ m_sample_address[chan] = (m_sample_address[chan] & 0xffff0000) | data;
if(0)
- logerror("snd chan %02x format %s flags %02x address %06x\n", chan, formats[m_address[chan] >> 30], (m_address[chan] >> 24) & 0x3f, m_address[chan] & 0xffffff);
+ logerror("snd chan %02x format %s flags %02x address %06x\n", chan, formats[m_sample_address[chan] >> 30], (m_sample_address[chan] >> 24) & 0x3f, m_sample_address[chan] & 0xffffff);
}
u16 swp30_device::internal_adr_r()
@@ -782,19 +1095,17 @@ u16 swp30_device::internal_r()
u8 chan = m_internal_adr & 0x3f;
switch(m_internal_adr >> 8) {
case 0:
- return m_mode[chan] == IDLE ? 0xffff : m_current_volume[chan] >> (26-14);
+ // Not certain about the two top bits though, the code seems to only care about 0/non-0
+ return m_envelope_mode[chan] == IDLE ? 0xffff : ((m_envelope_mode[chan] - 1) << 14) | (m_envelope_level[chan] >> (28-14));
case 4:
// used at 44c4
// tests & 0x4000 only
// logerror("read %02x.4\n", chan);
- return 0xffff;
+ return 0x0000;
case 6:
- // used at 3e7c
- // tests & 0x8000 only, keyoff?
- logerror("read %02x.6\n", chan);
- return 0x0000;
+ return m_decay2_done[chan] ? 0x0000 : 0x8000;
}
logerror("%s internal_r port %x channel %02x sample %d\n", machine().time().to_string(), m_internal_adr >> 8, m_internal_adr & 0x1f, scount);
@@ -804,36 +1115,44 @@ u16 swp30_device::internal_r()
}
-// MEG registers forwarding
+// MEG registers
-template<int sel> u16 swp30_device::prg_fp_r(offs_t offset)
+template<int sel> u16 swp30_device::meg_const_r(offs_t offset)
{
- return m_meg->fp_r((offset >> 6)*6 + sel);
+ return m_meg_const[(offset >> 6)*6 + sel];
}
-template<int sel> void swp30_device::prg_fp_w(offs_t offset, u16 data)
+template<int sel> void swp30_device::meg_const_w(offs_t offset, u16 data)
{
- m_meg->fp_w((offset >> 6)*6 + sel, data);
+ m_meg_const[(offset >> 6)*6 + sel] = data;
}
-template<int sel> u16 swp30_device::prg_off_r(offs_t offset)
+template<int sel> u16 swp30_device::meg_offset_r(offs_t offset)
{
- return m_meg->offset_r((offset >> 6)*2 + sel);
+ return m_meg_offset[(offset >> 6)*2 + sel];
}
-template<int sel> void swp30_device::prg_off_w(offs_t offset, u16 data)
+template<int sel> void swp30_device::meg_offset_w(offs_t offset, u16 data)
{
- m_meg->offset_w((offset >> 6)*2 + sel, data);
+ m_meg_offset[(offset >> 6)*2 + sel] = data;
}
-template<int sel> u16 swp30_device::prg_lfo_r(offs_t offset)
+template<int sel> u16 swp30_device::meg_lfo_r(offs_t offset)
{
- return m_meg->lfo_r((offset >> 6)*2 + sel);
+ return m_meg_lfo[(offset >> 6)*2 + sel];
}
-template<int sel> void swp30_device::prg_lfo_w(offs_t offset, u16 data)
+template<int sel> void swp30_device::meg_lfo_w(offs_t offset, u16 data)
{
- m_meg->lfo_w((offset >> 6)*2 + sel, data);
+ int slot = (offset >> 6)*2 + sel;
+ m_meg_lfo[slot] = data;
+
+ static const int dt[8] = { 0, 32, 64, 128, 256, 512, 1024, 2048 };
+ static const int sh[8] = { 0, 0, 1, 2, 3, 4, 5, 6 };
+
+ int scale = (data >> 5) & 7;
+ int step = ((data & 31) << sh[scale]) + dt[scale];
+ logerror("lfo_w %02x %04x freq=%5.2f phase=%6.4f\n", slot, m_meg_lfo[slot], step * 44100.0/4194304, (data >> 8)/256.0);
}
@@ -858,8 +1177,6 @@ u16 swp30_device::snd_r(offs_t offset)
preg = util::string_format("%02x.%02x", chan, slot);
logerror("snd_r [%04x %04x] %-5s, %04x\n", offset, offset*2, preg, rr[offset]);
}
- if(0 && offset == 0x080f)
- machine().debug_break();
if(offset == 0x080f)
return 0;
return rr[offset];
@@ -878,9 +1195,6 @@ void swp30_device::snd_w(offs_t offset, u16 data)
if(offset == 0x04e)
return;
- if(0 && slot == 0x03)
- machine().debug_break();
-
std::string preg = "-";
if(slot >= 0x21 && slot <= 0x2b && (slot & 1))
preg = util::string_format("fp%03x", (slot-0x21)/2 + 6*chan);
@@ -888,210 +1202,505 @@ void swp30_device::snd_w(offs_t offset, u16 data)
preg = util::string_format("sy%02x", (slot-0x0e) + 2*chan);
else if(slot == 0x30 || slot == 0x31)
preg = util::string_format("dt%02x", (slot-0x30) + 2*chan);
- else if(slot == 0x38)
- preg = util::string_format("vl%02x", chan);
+ else if(slot >= 0x38 && slot <= 0x3a)
+ preg = util::string_format("mix[%x, %02x]", slot - 0x38, chan);
+ else if(slot >= 0x3b && slot <= 0x3d)
+ preg = util::string_format("route[%x, %02x]", slot - 0x3b, chan);
else if(slot == 0x3e || slot == 0x3f)
- preg = util::string_format("lf%02x", (slot-0x3e) + 2*chan);
+ preg = util::string_format("lfo[%02x]", (slot-0x3e) + 2*chan);
else
preg = util::string_format("%02x.%02x", chan, slot);
- // if((slot >= 0xa && slot <= 0xd) || (slot >= 0x2c && slot <= 0x2f))
- // machine().debug_break();
logerror("snd_w [%04x %04x] %-5s, %04x\n", offset, offset*2, preg, data);
}
-// Synthesis
+// Synthesis and meg
-void swp30_device::change_mode(int channel, u8 mode)
+uint32_t swp30_device::execute_min_cycles() const noexcept
{
- if(1)
- logerror("[%08d] channel %02x mode %s\n", scount, channel,
- mode == IDLE ? "idle" :
- mode == ATTACK ? "attack" :
- mode == DECAY ? "decay" :
- mode == SUSTAIN ? "sustain" :
- mode == RELEASE ? "release" :
- "?");
+ return 1;
+}
- m_mode[channel] = mode;
- if(mode == IDLE || mode == SUSTAIN) {
- m_step_volume[channel] = 0;
- return;
- }
+uint32_t swp30_device::execute_max_cycles() const noexcept
+{
+ return 1;
+}
- u16 reg = mode == ATTACK ? m_attack[channel] & 0xff00 : mode == DECAY ? m_decay[channel] : m_release[channel];
- u32 target = (reg & 0xff) << (26-8);
- s32 step = (8 + ((reg >> 8) & 7)) << ((reg >> 11) & 15);
- if(mode != ATTACK)
- step *= 8;
- if(target < m_current_volume[channel])
- step = -step;
- m_target_volume[channel] = target;
- m_step_volume[channel] = step;
-
- if(1) {
- double delay = (double(target) - double(m_current_volume[channel])) / (44100*step);
- logerror(" -> time until hit %f seconds\n", delay);
- }
+uint32_t swp30_device::execute_input_lines() const noexcept
+{
+ return 0;
+}
+
+
+void swp30_device::meg_prg_map(address_map &map)
+{
+ map(0x000, 0x1bf).r(FUNC(swp30_device::meg_prg_map_r));
+}
+
+u64 swp30_device::meg_prg_map_r(offs_t address)
+{
+ return m_meg_program[address];
+}
+
+void swp30_device::meg_reverb_map(address_map &map)
+{
+ map(0x00000, 0x3ffff).ram();
+}
+
+u16 swp30_device::swp30d_const_r(u16 address) const
+{
+ return m_meg_const[address];
+}
+
+u16 swp30_device::swp30d_offset_r(u16 address) const
+{
+ return m_meg_offset[address];
+}
+
+device_memory_interface::space_config_vector swp30_device::memory_space_config() const
+{
+ return space_config_vector {
+ std::make_pair(AS_PROGRAM, &m_program_config),
+ std::make_pair(AS_DATA, &m_rom_config),
+ std::make_pair(AS_REVERB, &m_reverb_config),
+ };
+}
+
+std::unique_ptr<util::disasm_interface> swp30_device::create_disassembler()
+{
+ return std::make_unique<swp30_disassembler>(this);
+}
+
+void swp30_device::state_import(const device_state_entry &entry)
+{
+}
+
+void swp30_device::state_export(const device_state_entry &entry)
+{
+}
+
+void swp30_device::state_string_export(const device_state_entry &entry, std::string &str) const
+{
}
void swp30_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
{
- if(outputs[0].samples() != 1)
- fatalerror("Sync stream not sync?\n");
-
- scount++;
-
- // Accumulate on 64 bits, shift/clamp at the end
- s64 dry_left = 0, dry_right = 0;
-
- // Loop on channels
- for(int channel = 0; channel < 64; channel++)
- if(m_mode[channel] != IDLE) {
- // First, read the sample
-
- // - Find the base sample index and base address
- s32 spos = m_sample_pos[channel] >> 8;
- offs_t base_address = (m_address[channel] & 0x1ffffff) << 2;
- // - Read/decompress the sample
- s16 samp = 0;
- switch(m_address[channel] >> 30) {
- case 0: { // 16-bits linear
- offs_t adr = base_address + (spos << 1);
- samp = read_word(adr);
- break;
+ outputs[0].put_int_clamp(0, m_meg_output[0], 32768);
+ outputs[1].put_int_clamp(0, m_meg_output[1], 32768);
+}
+
+void swp30_device::change_mode_attack_decay1(int chan)
+{
+ m_envelope_mode[chan] = DECAY1;
+ m_envelope_timer[chan] = 0x8000000;
+ m_envelope_on_timer[chan] = (m_decay1[chan] & 0xff) == 0;
+}
+
+void swp30_device::change_mode_decay1_decay2(int chan)
+{
+ m_envelope_mode[chan] = DECAY2;
+ m_envelope_timer[chan] = 0x8000000;
+ m_envelope_on_timer[chan] = (m_decay2[chan] & 0xff) == (m_decay1[chan] & 0xff);
+}
+
+s32 swp30_device::meg_att(s32 sample, s32 att)
+{
+ if(att >= 0xff)
+ return 0;
+ return (sample - ((sample * (att & 0xf)) >> 4)) >> (att >> 4);
+
+}
+
+void swp30_device::execute_run()
+{
+ while(m_icount >= 0) {
+ if(m_meg_pc == 0) {
+ scount++;
+ if(0) {
+ static std::array<mixer_slot, 0x80> mixer;
+ if(memcmp(mixer.data(), m_mixer.data(), sizeof(mixer))) {
+ mixer = m_mixer;
+ for(int i=0; i != 0x20; i++) {
+ logerror("mixer %02x %04x.%04x.%04x %04x.%04x.%04x %02x %04x.%04x.%04x %04x.%04x.%04x %02x %04x.%04x.%04x %04x.%04x.%04x %02x %04x.%04x.%04x %04x.%04x.%04x\n",
+ 0x00 | i, m_mixer[0x00|i].vol[0], m_mixer[0x00|i].vol[1], m_mixer[0x00|i].vol[2], m_mixer[0x00|i].route[0], m_mixer[0x00|i].route[1], m_mixer[0x00|i].route[2],
+ 0x20 | i, m_mixer[0x20|i].vol[0], m_mixer[0x20|i].vol[1], m_mixer[0x20|i].vol[2], m_mixer[0x20|i].route[0], m_mixer[0x20|i].route[1], m_mixer[0x20|i].route[2],
+ 0x40 | i, m_mixer[0x40|i].vol[0], m_mixer[0x40|i].vol[1], m_mixer[0x40|i].vol[2], m_mixer[0x40|i].route[0], m_mixer[0x40|i].route[1], m_mixer[0x40|i].route[2],
+ 0x60 | i, m_mixer[0x60|i].vol[0], m_mixer[0x60|i].vol[1], m_mixer[0x60|i].vol[2], m_mixer[0x60|i].route[0], m_mixer[0x60|i].route[1], m_mixer[0x60|i].route[2]);
+ }
+ }
}
- case 1: { // 12-bits linear
- offs_t adr = base_address + (spos >> 2)*6;
- switch(spos & 3) {
- case 0: { // .abc .... ....
- u16 w0 = read_word(adr);
- samp = (w0 & 0x0fff) << 4;
+
+ // AWM2 synthesis
+ s32 samples_per_chan[0x40];
+ for(int chan = 0; chan != 0x40; chan++) {
+ if(m_envelope_mode[chan] == IDLE) {
+ samples_per_chan[chan] = 0;
+ continue;
+ }
+
+ // There actually are three shapes (0000, 4000 and c000) but
+ // we're not sure what they are
+
+ u32 lfo_phase = m_lfo_phase[chan] >> 7;
+ s32 lfo_p_phase = lfo_phase ^ (m_lfo_step_pmod[chan] & 0xc000 ? lfo_shape_centered_tri : lfo_shape_centered_saw)[lfo_phase >> 18];
+ s32 lfo_a_phase = lfo_phase ^ (m_lfo_step_pmod[chan] & 0xc000 ? lfo_shape_offset_tri : lfo_shape_offset_saw )[lfo_phase >> 18];
+
+ lfo_p_phase = lfo_a_phase = 0;
+
+ // First, read the sample
+
+ // - Find the base sample index and base address
+ s32 sample_pos = m_sample_pos[chan];
+ if(m_sample_end[chan] & 0x80000000)
+ sample_pos = -sample_pos;
+
+ s32 spos = sample_pos >> 8;
+ offs_t base_address = m_sample_address[chan] & 0x1ffffff;
+
+ // - Read/decompress the sample
+ s16 val0, val1;
+ switch(m_sample_address[chan] >> 30) {
+ case 0: { // 16-bits linear
+ offs_t adr = base_address + (spos >> 1);
+ switch(spos & 1) {
+ case 0: { // ABCDabcd ........
+ u32 l0 = m_rom_cache.read_dword(adr);
+ val0 = l0;
+ val1 = l0 >> 16;
+ break;
+ }
+ case 1: { // abcd.... ....ABCD
+ u32 l0 = m_rom_cache.read_dword(adr);
+ u32 l1 = m_rom_cache.read_dword(adr+1);
+ val0 = l0 >> 16;
+ val1 = l1;
+ break;
+ }
+ }
break;
}
- case 1: { // C... ..AB ....
- u16 w0 = read_word(adr);
- u16 w1 = read_word(adr+2);
- samp = ((w0 & 0xf000) >> 8) | ((w1 & 0x00ff) << 8);
+
+ case 1: { // 12-bits linear
+ offs_t adr = base_address + (spos >> 3)*3;
+ switch(spos & 7) {
+ case 0: { // ..ABCabc ........ ........ ........
+ u32 l0 = m_rom_cache.read_dword(adr);
+ val0 = (l0 & 0x00000fff) << 4;
+ val1 = (l0 & 0x00fff000) >> 8;
+ break;
+ }
+ case 1: { // BCabc... .......A ........ ........
+ u32 l0 = m_rom_cache.read_dword(adr);
+ u32 l1 = m_rom_cache.read_dword(adr+1);
+ val0 = (l0 & 0x00fff000) >> 8;
+ val1 = ((l0 & 0xff000000) >> 20) | ((l1 & 0x0000000f) << 12);
+ break;
+ }
+ case 2: { // bc...... ....ABCa ........ ........
+ u32 l0 = m_rom_cache.read_dword(adr);
+ u32 l1 = m_rom_cache.read_dword(adr+1);
+ val0 = ((l0 & 0xff000000) >> 20) | ((l1 & 0x0000000f) << 12);
+ val1 = l1 & 0x0000fff0;
+ break;
+ }
+ case 3: { // ........ .ABCabc. ........ ........
+ u32 l1 = m_rom_cache.read_dword(adr+1);
+ val0 = l1 & 0x0000fff0;
+ val1 = (l1 & 0x0fff0000) >> 12;
+ break;
+ }
+ case 4: { // ........ Cabc.... ......AB ........
+ u32 l1 = m_rom_cache.read_dword(adr+1);
+ u32 l2 = m_rom_cache.read_dword(adr+2);
+ val0 = (l1 & 0x0fff0000) >> 12;
+ val1 = ((l1 & 0xf0000000) >> 24) | ((l2 & 0x000000ff) << 8);
+ break;
+ }
+ case 5: { // ........ c....... ...ABCab ........
+ u32 l1 = m_rom_cache.read_dword(adr+1);
+ u32 l2 = m_rom_cache.read_dword(adr+2);
+ val0 = ((l1 & 0xf0000000) >> 24) | ((l2 & 0x000000ff) << 8);
+ val1 = (l2 & 0x000fff00) >> 4;
+ break;
+ }
+ case 6: { // ........ ........ ABCabc.. ........
+ u32 l2 = m_rom_cache.read_dword(adr+2);
+ val0 = (l2 & 0x000fff00) >> 4;
+ val1 = (l2 & 0xfff00000) >> 16;
+ break;
+ }
+ case 7: { // ........ ........ abc..... .....ABC
+ u32 l2 = m_rom_cache.read_dword(adr+2);
+ u32 l3 = m_rom_cache.read_dword(adr+3);
+ val0 = (l2 & 0xfff00000) >> 16;
+ val1 = (l3 & 0x00000fff) << 4;
+ break;
+ }
+ }
+ logerror("XX %04x %04x\n", val0, val1);
break;
}
- case 2: { // .... bc.. ...a
- u16 w0 = read_word(adr+2);
- u16 w1 = read_word(adr+4);
- samp = ((w0 & 0xff00) >> 4) | ((w1 & 0x000f) << 12);
+
+ case 2: { // 8-bits linear
+ offs_t adr = base_address + (spos >> 2);
+ switch(spos & 3) {
+ case 0: { // ....ABab ........
+ u32 l0 = m_rom_cache.read_dword(adr);
+ val0 = (l0 & 0x000000ff) << 8;
+ val1 = l0 & 0x0000ff00;
+ break;
+ }
+ case 1: { // ..ABab.. ........
+ u32 l0 = m_rom_cache.read_dword(adr);
+ val0 = l0 & 0x0000ff00;
+ val1 = (l0 & 0x00ff0000) >> 8;
+ break;
+ }
+ case 2: { // ABab.... ........
+ u32 l0 = m_rom_cache.read_dword(adr);
+ val0 = (l0 & 0x00ff0000) >> 8;
+ val1 = (l0 & 0xff000000) >> 16;
+ break;
+ }
+ case 3: { // ab...... ......AB
+ u32 l0 = m_rom_cache.read_dword(adr);
+ u32 l1 = m_rom_cache.read_dword(adr+1);
+ val0 = (l0 & 0xff000000) >> 16;
+ val1 = (l1 & 0x000000ff) << 8;
+ break;
+ }
+ }
break;
}
- case 3: { // .... .... ABC.
- u16 w1 = read_word(adr+4);
- samp = w1 & 0xfff0;
+
+ case 3: { // 8-bits delta-pcm
+ offs_t adr = m_dpcm_address[chan];
+ if(m_sample_end[chan] & 0x80000000) {
+ u32 target_address = (base_address << 2) + spos - 1;
+ while(adr >= target_address) {
+ m_dpcm_current[chan] = m_dpcm_next[chan];
+ s32 sample = m_dpcm_next[chan] + m_dpcm[(m_rom_cache.read_dword(adr >> 2) >> (8*(adr & 3))) & 0xff];
+ adr --;
+ if(sample < -0x8000)
+ sample = -0x8000;
+ else if(sample > 0x7fff)
+ sample = 0x7fff;
+ m_dpcm_next[chan] = sample;
+ }
+ } else {
+ u32 target_address = (base_address << 2) + spos + 1;
+ while(adr <= target_address) {
+ m_dpcm_current[chan] = m_dpcm_next[chan];
+ s32 sample = m_dpcm_next[chan] + m_dpcm[(m_rom_cache.read_dword(adr >> 2) >> (8*(adr & 3))) & 0xff];
+ // logerror("## + sample %08x %02x %d\n", adr, (m_rom_cache.read_dword(adr >> 2) >> (8*(adr & 3))) & 0xff, sample);
+ adr ++;
+ if(sample < -0x8000)
+ sample = -0x8000;
+ else if(sample > 0x7fff)
+ sample = 0x7fff;
+ m_dpcm_next[chan] = sample;
+ }
+ }
+ m_dpcm_address[chan] = adr;
+ val0 = m_dpcm_current[chan];
+ val1 = m_dpcm_next[chan];
break;
}
}
- break;
- }
- case 2: // 8-bits linear
- samp = read_byte(base_address + spos) << 8;
- break;
+ s32 mul = sample_pos & 0xff;
+ s32 sample = val1 * mul + val0 * (0x100 - mul);
- case 3: // 8-bits logarithmic
- samp = m_sample_log8[read_byte(base_address + spos)];
- break;
- }
+#if 0
+ // Third, filter the sample
+ // - missing lpf_cutoff, lpf_reso, hpf_cutoff
- //logerror("sample %02x %06x [%d] %+5d %04x %04x %04x\n", channel, base_address >> 2, m_address[channel] >> 30, spos, samp & 0xffff, m_volume[channel], m_pan[channel]);
+ // - eq lowpass
+ s32 samp1 = (samp * m_eq_filter[chan][2] + m_sample_history[chan][0][0] * m_eq_filter[chan][1] + m_sample_history[chan][0][1] * m_eq_filter[chan][0]) >> 13;
+ m_sample_history[chan][0][1] = m_sample_history[chan][0][0];
+ m_sample_history[chan][0][0] = samp;
- // Second, step the sample pos, loop/deactivate as needed
- m_sample_pos[channel] += m_sample_increment[m_freq[channel] & 0x3fff];
- s32 loop_size = (m_post_size[channel] << 8) | ((m_address[channel] >> 22) & 0xf8);
- if(m_sample_pos[channel] >= loop_size) {
- // We reached the loop point, stop if loop size is zero,
- // otherwise loop
- if(!loop_size)
- change_mode(channel, IDLE);
- else
- do
- m_sample_pos[channel] -= loop_size;
- while(m_sample_pos[channel] >= loop_size);
- }
+ // - eq highpass
+ s32 samp2 = (samp1 * m_eq_filter[chan][5] + m_sample_history[chan][1][0] * m_eq_filter[chan][4] + m_sample_history[chan][1][1] * m_eq_filter[chan][3]) >> 13;
+ m_sample_history[chan][1][1] = m_sample_history[chan][1][0];
+ m_sample_history[chan][1][0] = samp1;
- // Third, filter the sample
- // - missing lpf_cutoff, lpf_reso, hpf_cutoff
+#endif
- // - eq lowpass
- s32 samp1 = (samp * m_eq_filter[channel][2] + m_sample_history[channel][0][0] * m_eq_filter[channel][1] + m_sample_history[channel][0][1] * m_eq_filter[channel][0]) >> 13;
- m_sample_history[channel][0][1] = m_sample_history[channel][0][0];
- m_sample_history[channel][0][0] = samp;
+ s32 tremolo_level = (lfo_a_phase * (m_lfo_amod[chan] & 0x1f)) << ((m_lfo_step_pmod[chan] & 0xc000) ? 3 : 2);
- // - eq highpass
- s32 samp2 = (samp1 * m_eq_filter[channel][5] + m_sample_history[channel][1][0] * m_eq_filter[channel][4] + m_sample_history[channel][1][1] * m_eq_filter[channel][3]) >> 13;
- m_sample_history[channel][1][1] = m_sample_history[channel][1][0];
- m_sample_history[channel][1][0] = samp1;
+ samples_per_chan[chan] = fpapply(m_envelope_level[chan] + (m_glo_level_cur[chan] << 16) + tremolo_level, sample) >> 8;
- // Fourth, establish the 8 volumes (only 2 for now, need the MEG) and update the envelope
- u32 raw_vol = m_current_volume[channel];
- u32 vol = raw_vol >> (26-8);
+ istep(m_glo_level_cur[chan], (m_release_glo[chan] & 0x00ff) << 4, 1);
- u32 base_l = vol + (m_pan[channel] >> 8);
- u32 base_r = vol + (m_pan[channel] & 0xff);
+ m_lfo_phase[chan] = (m_lfo_phase[chan] + m_global_step[0x20 + ((m_lfo_step_pmod[chan] >> 8) & 0x3f)]) & 0x7ffffff;
- u32 dry_l = base_l + (m_dry_rev[channel] >> 8);
- u32 dry_r = base_r + (m_dry_rev[channel] >> 8);
+ u32 sample_increment = pitch_base[m_pitch[chan] & 0x3ff] >> (23 - ((s16(m_pitch[chan] << 2) >> 12)));
+ m_sample_pos[chan] += (sample_increment * (0x800 + ((lfo_p_phase * (m_lfo_step_pmod[chan] & 0xff)) >> (m_lfo_step_pmod[chan] & 0xc000 ? 18 : 19)))) >> 11;
+ if((m_sample_pos[chan] >> 8) >= (m_sample_end[chan] & 0xffffff)) {
+ if(!(m_sample_end[chan] & 0xffffff))
+ m_envelope_mode[chan] = IDLE;
+ else {
+ s32 prev = m_sample_pos[chan];
+ do
+ m_sample_pos[chan] = m_sample_pos[chan] - ((m_sample_end[chan] & 0xffffff) << 8) + ((m_sample_address[chan] >> 22) & 0xfc);
+ while((m_sample_pos[chan] >> 8) >= (m_sample_end[chan] & 0xffffff));
+ if(m_sample_end[chan] & 0x80000000)
+ m_dpcm_address[chan] -= (m_sample_pos[chan] >> 8) - (prev >> 8);
+ else
+ m_dpcm_address[chan] += (m_sample_pos[chan] >> 8) - (prev >> 8);
+ }
+ }
- s32 step = m_step_volume[channel];
- u32 target = m_target_volume[channel];
+ switch(m_envelope_mode[chan]) {
+ case ATTACK:
+ if(m_envelope_on_timer[chan]) {
+ if(istep(m_envelope_timer[chan], 0, m_global_step[(m_attack[chan] >> 8) & 0x7f] << 1))
+ change_mode_attack_decay1(chan);
+ } else {
+ if(fpstep(m_envelope_level[chan], 0, attack_linear_step[(m_attack[chan] >> 8) & 0x7f]))
+ change_mode_attack_decay1(chan);
+ }
+ break;
- if(0) {
- u8 mode = m_mode[channel];
- logerror("[%08d] channel %02x state %s vol=%07x step=%c%07x target=%07x\n",
- scount,
- channel,
- mode == IDLE ? "idle" :
- mode == ATTACK ? "attack" :
- mode == DECAY ? "decay" :
- mode == SUSTAIN ? "sustain" :
- mode == RELEASE ? "release" :
- "?",
- raw_vol,
- step < 0 ? '-' : '+',
- step < 0 ? -step : step,
- target);
- }
- if(step) {
- raw_vol += step;
- if((step < 0 && (raw_vol <= target || raw_vol & 0x80000000)) ||
- (step > 0 && raw_vol >= target)) {
- raw_vol = target;
- m_current_volume[channel] = raw_vol;
-
- // IDLE and SUSTAIN have zero step.
- // current volume must be updated before calling change_mode
-
- switch(m_mode[channel]) {
- case ATTACK: change_mode(channel, DECAY); break;
- case DECAY: change_mode(channel, SUSTAIN); break;
- case RELEASE: change_mode(channel, IDLE); break;
+ case DECAY1:
+ if(m_envelope_on_timer[chan]) {
+ if(istep(m_envelope_timer[chan], 0, m_global_step[(m_decay1[chan] >> 8) & 0x7f] << 1))
+ change_mode_decay1_decay2(chan);
+ } else if((m_decay1[chan] & 0x6000) == 0x6000) {
+ if(fpstep(m_envelope_level[chan], (m_decay1[chan] & 0xff) << 20, decay_linear_step[(m_decay1[chan] >> 8) & 0x1f]))
+ change_mode_decay1_decay2(chan);
+ } else {
+ if(fpstep(m_envelope_level[chan], (m_decay1[chan] & 0xff) << 20, m_global_step[(m_decay1[chan] >> 8) & 0x7f]))
+ change_mode_decay1_decay2(chan);
+ }
+ break;
+
+ case DECAY2:
+ if(m_envelope_on_timer[chan])
+ m_decay2_done[chan] = istep(m_envelope_timer[chan], 0, m_global_step[(m_decay1[chan] >> 8) & 0x7f] << 1);
+ else if((m_decay2[chan] & 0x6000) == 0x6000)
+ m_decay2_done[chan] = fpstep(m_envelope_level[chan], (m_decay2[chan] & 0xff) << 20, decay_linear_step[(m_decay2[chan] >> 8) & 0x1f]);
+ else
+ m_decay2_done[chan] = fpstep(m_envelope_level[chan], (m_decay2[chan] & 0xff) << 20, m_global_step[(m_decay2[chan] >> 8) & 0x7f]);
+ break;
+
+ case RELEASE:
+ if((m_release_glo[chan] & 0x6000) == 0x6000) {
+ if(fpstep(m_envelope_level[chan], 0x8000000, decay_linear_step[(m_release_glo[chan] >> 8) & 0x1f]))
+ m_envelope_mode[chan] = IDLE;
+ } else {
+ if(fpstep(m_envelope_level[chan], 0x8000000, m_global_step[(m_release_glo[chan] >> 8) & 0x7f]))
+ m_envelope_mode[chan] = IDLE;
}
- } else
- m_current_volume[channel] = raw_vol;
+ break;
+ }
}
- // Fifth, add to the (dry) accumulators
- dry_left += samp2 * m_linear_attenuation[std::min(0xffu, dry_l)];
- dry_right += samp2 * m_linear_attenuation[std::min(0xffu, dry_r)];
+ // Mixer
+ std::array<s32, 0x10> out_samples;
+ std::copy(m_meg_m.begin() + 0x20, m_meg_m.begin() + 0x30, out_samples.begin());
+ std::fill(m_meg_m.begin() + 0x20, m_meg_m.begin() + 0x30, 0);
+ std::fill(m_melo.begin(), m_melo.end(), 0);
+ std::fill(m_meg_output.begin(), m_meg_output.end(), 0);
+
+ for(int mix = 0; mix != 0x60; mix++) {
+ s32 input;
+ if(mix < 0x40)
+ input = samples_per_chan[mix];
+ else if(mix < 0x50)
+ input = out_samples[mix & 0xf];
+ else
+ input = 0; // Audio input not yet supported in Mame (meli 0-7)
+
+ if(input == 0)
+ continue;
+
+ u64 route = (u64(m_mixer[mix].route[0]) << 32) | (u64(m_mixer[mix].route[1]) << 16) | m_mixer[mix].route[2];
+ const std::array<u16, 3> &vol = m_mixer[mix].vol;
+
+ // It looks like this could be turned into something generic, but not 100% clear
+ // routes 000100010001, 000200020002 etc seem to target the melo ports
+ switch(route) {
+ case 0x000000000000:
+ // Incorrect, the program writes the outputs to
+ // m30/m31, but right now the program doesn't run.
+ m_meg_output[0] += meg_att(input, (vol[0] >> 8) + (vol[1] >> 8));
+ m_meg_output[1] += meg_att(input, (vol[0] & 0xff) + (vol[1] >> 8));
+ break;
- // Missing: reverb, chorus, effects in general
- }
+ case 0x000100000000: // Used by the mu90, which does not write to 30/31
+ m_meg_output[0] += meg_att(input, (vol[0] >> 8) + (vol[1] >> 8));
+ m_meg_output[1] += meg_att(input, (vol[0] & 0xff) + (vol[1] >> 8));
+ break;
+
+ case 0x010000000000:
+ m_meg_m[0x20] += meg_att(input, (vol[0] >> 8) + (vol[1] >> 8));
+ m_meg_m[0x21] += meg_att(input, (vol[0] & 0xff) + (vol[1] >> 8));
+ break;
+
+ case 0x020000000000:
+ m_meg_m[0x22] += meg_att(input, (vol[0] >> 8) + (vol[1] >> 8));
+ m_meg_m[0x23] += meg_att(input, (vol[0] & 0xff) + (vol[1] >> 8));
+ break;
- // Samples are 16 bits, there are up to 64 of them, and the accumulators are fixed-point signed 48.16
- // Global EQ is missing (it's done in the MEG)
+ case 0x050000000400:
+ m_meg_m[0x20] += meg_att(input, (vol[0] >> 8) + (vol[1] >> 8));
+ m_meg_m[0x21] += meg_att(input, (vol[0] & 0xff) + (vol[1] >> 8));
+ m_meg_m[0x24] += meg_att(input, (vol[0] >> 8) + (vol[1] & 0xff));
+ m_meg_m[0x25] += meg_att(input, (vol[0] & 0xff) + (vol[1] & 0xff));
+ break;
- dry_left >>= 14;
- outputs[0].put_int_clamp(0, dry_left, 32768);
- dry_right >>= 14;
- outputs[1].put_int_clamp(0, dry_right, 32768);
+ case 0x0d0008000400:
+ m_meg_m[0x20] += meg_att(input, (vol[0] >> 8) + (vol[1] >> 8));
+ m_meg_m[0x21] += meg_att(input, (vol[0] & 0xff) + (vol[1] >> 8));
+ m_meg_m[0x24] += meg_att(input, (vol[0] >> 8) + (vol[1] & 0xff));
+ m_meg_m[0x25] += meg_att(input, (vol[0] & 0xff) + (vol[1] & 0xff));
+ m_meg_m[0x26] += meg_att(input, (vol[0] >> 8) + (vol[2] >> 8));
+ m_meg_m[0x27] += meg_att(input, (vol[0] & 0xff) + (vol[2] >> 8));
+ break;
+
+ case 0x100010001000:
+ m_meg_m[0x28] += meg_att(input, (vol[0] >> 8) + (vol[1] >> 8));
+ m_meg_m[0x29] += meg_att(input, (vol[0] & 0xff) + (vol[1] >> 8));
+ break;
+
+ case 0x200020002000:
+ m_meg_m[0x2a] += meg_att(input, (vol[0] >> 8) + (vol[1] >> 8));
+ m_meg_m[0x2b] += meg_att(input, (vol[0] & 0xff) + (vol[1] >> 8));
+ break;
+
+ case 0x400040004000:
+ m_meg_m[0x2c] += meg_att(input, (vol[0] >> 8) + (vol[1] >> 8));
+ m_meg_m[0x2d] += meg_att(input, (vol[0] & 0xff) + (vol[1] >> 8));
+ break;
+
+ case 0x4d0048004400:
+ m_meg_m[0x20] += meg_att(input, (vol[0] >> 8) + (vol[1] >> 8));
+ m_meg_m[0x21] += meg_att(input, (vol[0] & 0xff) + (vol[1] >> 8));
+ m_meg_m[0x24] += meg_att(input, (vol[0] >> 8) + (vol[1] & 0xff));
+ m_meg_m[0x25] += meg_att(input, (vol[0] & 0xff) + (vol[1] & 0xff));
+ m_meg_m[0x26] += meg_att(input, (vol[0] >> 8) + (vol[2] >> 8));
+ m_meg_m[0x27] += meg_att(input, (vol[0] & 0xff) + (vol[2] >> 8));
+ m_meg_m[0x2c] += meg_att(input, (vol[0] >> 8) + (vol[2] & 0xff));
+ m_meg_m[0x2d] += meg_att(input, (vol[0] & 0xff) + (vol[2] & 0xff));
+ break;
+
+ default:
+ logerror("Unhandled route %012x\n", route);
+ break;
+ }
+ }
+ }
+
+ debugger_instruction_hook(m_meg_pc);
+ m_icount --;
+ m_meg_pc ++;
+ if(m_meg_pc == 0x180)
+ m_meg_pc = 0;
+ }
}
diff --git a/src/devices/sound/swp30.h b/src/devices/sound/swp30.h
index b83913b1562..6a2a8aff9b6 100644
--- a/src/devices/sound/swp30.h
+++ b/src/devices/sound/swp30.h
@@ -8,12 +8,13 @@
#pragma once
-#include "meg.h"
-#include "dirom.h"
+#include "swp30d.h"
-class swp30_device : public device_t, public device_sound_interface, public device_rom_interface<25+2, 2, 0, ENDIANNESS_LITTLE>
+class swp30_device : public cpu_device, public device_sound_interface, public swp30_disassembler::info
{
public:
+ enum { AS_REVERB = AS_IO };
+
swp30_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 33868800);
void map(address_map &map);
@@ -22,52 +23,102 @@ protected:
virtual void device_start() override;
virtual void device_reset() override;
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
- virtual void rom_bank_updated() override;
- virtual void device_add_mconfig(machine_config &config) override;
+ virtual uint32_t execute_min_cycles() const noexcept override;
+ virtual uint32_t execute_max_cycles() const noexcept override;
+ virtual uint32_t execute_input_lines() const noexcept override;
+ virtual uint64_t execute_clocks_to_cycles(uint64_t clocks) const noexcept override { return (clocks + 1) / 2; }
+ virtual void execute_run() override;
+ virtual space_config_vector memory_space_config() const override;
+ virtual void state_import(const device_state_entry &entry) override;
+ virtual void state_export(const device_state_entry &entry) override;
+ virtual void state_string_export(const device_state_entry &entry, std::string &str) const override;
+ virtual std::unique_ptr<util::disasm_interface> create_disassembler() override;
private:
enum {
IDLE,
ATTACK,
- DECAY,
- SUSTAIN,
+ DECAY1,
+ DECAY2,
RELEASE
};
- required_device<meg_embedded_device> m_meg;
+ struct mixer_slot {
+ std::array<u16, 3> vol;
+ std::array<u16, 3> route;
+ };
+
+ address_space_config m_program_config, m_rom_config, m_reverb_config;
+ address_space *m_program, *m_rom, *m_reverb;
+ memory_access<25, 2, -2, ENDIANNESS_LITTLE>::cache m_rom_cache;
+ memory_access<18, 1, -1, ENDIANNESS_LITTLE>::cache m_reverb_cache;
sound_stream *m_stream;
- s32 m_sample_increment[0x4000];
- s32 m_linear_attenuation[0x100];
- s16 m_sample_log8[0x100];
+ static const std::array<s32, 0x80> attack_linear_step;
+ static const std::array<s32, 0x20> decay_linear_step;
+ static const std::array<s32, 16> panmap;
+ static const std::array<u32, 0x400> pitch_base;
+ std::array<s32, 0x80> m_global_step;
+ std::array<s16, 0x100> m_dpcm;
+
+ static const std::array<u32, 4> lfo_shape_centered_saw;
+ static const std::array<u32, 4> lfo_shape_centered_tri;
+ static const std::array<u32, 4> lfo_shape_offset_saw;
+ static const std::array<u32, 4> lfo_shape_offset_tri;
+
+ std::array<s32, 0x40> m_sample_start;
+ std::array<s32, 0x40> m_sample_end;
+ std::array<u32, 0x40> m_sample_address;
+ std::array<u16, 0x40> m_pitch;
+
+ std::array<u16, 0x40> m_attack;
+ std::array<u16, 0x40> m_decay1;
+ std::array<u16, 0x40> m_decay2;
+ std::array<u16, 0x40> m_release_glo;
+ std::array<u16, 0x40> m_lfo_step_pmod;
+ std::array<u16, 0x40> m_lfo_amod;
+
+ std::array<u32, 0x40> m_lfo_phase;
+ std::array<s32, 0x40> m_sample_pos;
+ std::array<s32, 0x40> m_envelope_level;
+ std::array<s32, 0x40> m_envelope_timer;
+ std::array<bool, 0x40> m_envelope_on_timer;
+ std::array<bool, 0x40> m_decay2_done;
+ std::array<u8, 0x40> m_envelope_mode;
+ std::array<s32, 0x40> m_glo_level_cur;
+ std::array<s32, 0x40> m_pan_l;
+ std::array<s32, 0x40> m_pan_r;
+ std::array<s16, 0x40> m_dpcm_current;
+ std::array<s16, 0x40> m_dpcm_next;
+ std::array<u32, 0x40> m_dpcm_address;
+
+ std::array<u64, 0x180> m_meg_program;
+ std::array<s16, 0x180> m_meg_const;
+ std::array<u16, 0x80> m_meg_offset;
+ std::array<u16, 0x18> m_meg_lfo;
+ std::array<u16, 8> m_meg_map;
+
+ std::array<mixer_slot, 0x80> m_mixer;
+
+ std::array<s32, 0x40> m_meg_m;
+ std::array<s32, 0x10> m_melo;
+ std::array<s32, 2> m_meg_output;
- u64 m_program[0x180];
- u64 m_keyon_mask;
- u32 m_pre_size[0x40], m_post_size[0x40], m_address[0x40];
-
- s32 m_sample_pos[0x40];
s32 m_sample_history[0x40][2][2];
- u32 m_current_volume[0x40], m_target_volume[0x40];
- s32 m_step_volume[0x40];
u32 m_waverom_adr, m_waverom_mode, m_waverom_val;
u16 m_waverom_access;
- u16 m_program_pfp[0x180], m_program_pint[0x80], m_program_plfo[0x80];
-
- u16 m_base_volume[0x40], m_freq[0x40], m_pan[0x40], m_dry_rev[0x40], m_cho_var[0x40];
- u16 m_attack[0x40], m_decay[0x40], m_release[0x40];
u16 m_lpf_cutoff[0x40], m_lpf_cutoff_inc[0x40], m_lpf_reso[0x40], m_hpf_cutoff[0x40];
s16 m_eq_filter[0x40][6];
- u16 m_routing[0x40][3];
- u16 m_map[8];
+ u64 m_keyon_mask;
u16 m_internal_adr;
- u16 m_program_address;
-
- u8 m_mode[0x40];
+ u16 m_meg_program_address;
+ u16 m_meg_pc;
+ int m_icount;
// AWM2 per-channel registers
u16 lpf_cutoff_r(offs_t offset);
@@ -80,54 +131,72 @@ private:
void lpf_reso_w(offs_t offset, u16 data);
u16 attack_r(offs_t offset);
void attack_w(offs_t offset, u16 data);
- u16 decay_r(offs_t offset);
- void decay_w(offs_t offset, u16 data);
- u16 release_r(offs_t offset);
- void release_w(offs_t offset, u16 data);
+ u16 decay1_r(offs_t offset);
+ void decay1_w(offs_t offset, u16 data);
+ u16 decay2_r(offs_t offset);
+ void decay2_w(offs_t offset, u16 data);
+ u16 release_glo_r(offs_t offset);
+ void release_glo_w(offs_t offset, u16 data);
template<int coef> u16 eq_filter_r(offs_t offset);
template<int coef> void eq_filter_w(offs_t offset, u16 data);
- u16 base_volume_r(offs_t offset);
- void base_volume_w(offs_t offset, u16 data);
- u16 freq_r(offs_t offset);
- void freq_w(offs_t offset, u16 data);
- u16 pre_size_h_r(offs_t offset);
- u16 pre_size_l_r(offs_t offset);
- void pre_size_h_w(offs_t offset, u16 data);
- void pre_size_l_w(offs_t offset, u16 data);
- u16 post_size_h_r(offs_t offset);
- u16 post_size_l_r(offs_t offset);
- void post_size_h_w(offs_t offset, u16 data);
- void post_size_l_w(offs_t offset, u16 data);
- u16 address_h_r(offs_t offset);
- u16 address_l_r(offs_t offset);
- void address_h_w(offs_t offset, u16 data);
- void address_l_w(offs_t offset, u16 data);
+
+ u16 sample_start_h_r(offs_t offset);
+ u16 sample_start_l_r(offs_t offset);
+ void sample_start_h_w(offs_t offset, u16 data);
+ void sample_start_l_w(offs_t offset, u16 data);
+ u16 sample_end_h_r(offs_t offset);
+ u16 sample_end_l_r(offs_t offset);
+ void sample_end_h_w(offs_t offset, u16 data);
+ void sample_end_l_w(offs_t offset, u16 data);
+ u16 sample_address_h_r(offs_t offset);
+ u16 sample_address_l_r(offs_t offset);
+ void sample_address_h_w(offs_t offset, u16 data);
+ void sample_address_l_w(offs_t offset, u16 data);
+ u16 pitch_r(offs_t offset);
+ void pitch_w(offs_t offset, u16 data);
+
u16 pan_r(offs_t offset);
void pan_w(offs_t offset, u16 data);
u16 dry_rev_r(offs_t offset);
void dry_rev_w(offs_t offset, u16 data);
u16 cho_var_r(offs_t offset);
void cho_var_w(offs_t offset, u16 data);
+
+ void lfo_step_pmod_w(offs_t offset, u16 data);
+ u16 lfo_step_pmod_r(offs_t offset);
+ void lfo_amod_w(offs_t offset, u16 data);
+ u16 lfo_amod_r(offs_t offset);
+
u16 internal_adr_r();
void internal_adr_w(u16 data);
u16 internal_r();
- template<int sel> u16 routing_r(offs_t offset);
- template<int sel> void routing_w(offs_t offset, u16 data);
+ template<int sel> u16 route_r(offs_t offset);
+ template<int sel> void route_w(offs_t offset, u16 data);
+ template<int sel> u16 vol_r(offs_t offset);
+ template<int sel> void vol_w(offs_t offset, u16 data);
// Envelope control
- void change_mode(int channel, u8 mode);
+ void change_mode_attack_decay1(int chan);
+ void change_mode_decay1_decay2(int chan);
+ static bool istep(s32 &value, s32 limit, s32 step);
+ static bool fpstep(s32 &value, s32 limit, s32 step);
+ static s32 fpadd(s32 value, s32 step);
+ static s32 fpsub(s32 value, s32 step);
+ static s32 fpapply(s32 value, s32 sample);
+ static s32 lpffpapply(s32 value, s32 sample);
+ static s32 meg_att(s32 sample, s32 att);
// Control registers
template<int sel> u16 keyon_mask_r();
template<int sel> void keyon_mask_w(u16 data);
u16 keyon_r();
void keyon_w(u16);
- u16 prg_address_r();
- void prg_address_w(u16 data);
- template<int sel> u16 prg_r();
- template<int sel> void prg_w(u16 data);
- template<int sel> u16 map_r();
- template<int sel> void map_w(u16 data);
+ u16 meg_prg_address_r();
+ void meg_prg_address_w(u16 data);
+ template<int sel> u16 meg_prg_r();
+ template<int sel> void meg_prg_w(u16 data);
+ template<int sel> u16 meg_map_r();
+ template<int sel> void meg_map_w(u16 data);
template<int sel> void waverom_adr_w(u16 data);
template<int sel> u16 waverom_adr_r();
template<int sel> void waverom_mode_w(u16 data);
@@ -138,13 +207,20 @@ private:
template<int sel> u16 waverom_val_r();
// MEG registers
- template<int sel> u16 prg_fp_r(offs_t offset);
- template<int sel> void prg_fp_w(offs_t offset, u16 data);
- template<int sel> u16 prg_off_r(offs_t offset);
- template<int sel> void prg_off_w(offs_t offset, u16 data);
- template<int sel> u16 prg_lfo_r(offs_t offset);
- template<int sel> void prg_lfo_w(offs_t offset, u16 data);
+ template<int sel> u16 meg_const_r(offs_t offset);
+ template<int sel> void meg_const_w(offs_t offset, u16 data);
+ template<int sel> u16 meg_offset_r(offs_t offset);
+ template<int sel> void meg_offset_w(offs_t offset, u16 data);
+ template<int sel> u16 meg_lfo_r(offs_t offset);
+ template<int sel> void meg_lfo_w(offs_t offset, u16 data);
+
+ void meg_prg_map(address_map &map);
+ u64 meg_prg_map_r(offs_t address);
+
+ void meg_reverb_map(address_map &map);
+ virtual u16 swp30d_const_r(u16 address) const override;
+ virtual u16 swp30d_offset_r(u16 address) const override;
// Generic catch-all
u16 snd_r(offs_t offset);
diff --git a/src/devices/sound/swp30d.cpp b/src/devices/sound/swp30d.cpp
new file mode 100644
index 00000000000..df76426c462
--- /dev/null
+++ b/src/devices/sound/swp30d.cpp
@@ -0,0 +1,172 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Yamaha SWP30 - Multiple effects generator subpart
+//
+// Audio dsp dedicated to effects generation
+//
+// Disassembler
+
+// Known problems with the mu100 bios0 uploaded programs:
+//
+// At address 94 & 97, there's a missing r78 = p / r79 = p
+// respectively. Those are places where is both a rnn = p and mnn = p
+//
+// insertion1 rooms, at addresses cd, cf, d0, d2, d4, d5, dc, e3, e7
+// the r slots have data but are not used. No idea how they should be
+// used.
+//
+// Same for insertion2 rooms, addresses fd+
+//
+// Insertion 1/2 aural have a bunch of instructions (c7-ce, f7-fe)
+// using r02-r09 and r28/r3a which are not understood.
+//
+
+// Amplitude lfo is missing something. For instance variation delay
+// m34 is loaded at 120 and used at 13c, but the way it is used is not
+// decoded. In practice, we do not yet have a way to multiply two
+// registers together, only with a constant.
+
+// insertion phaser
+
+// y0 = x1 + (y1 - x0) * lfo
+
+// x = r4a
+// y = r4b
+
+// 14f:
+// 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000
+// 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210
+
+// XLB--ttt -rrrrrrr -xlmmmmm m-MM---- -P----** *--T-Arr rrrrrsmm mmmm----
+// 0000000003c25000 ........ ........ ........ ........ ......11 11....1. .1.1.... ........ p = 0 * x1
+// 0100000004925800 .......1 ........ ........ ........ .....1.. 1..1..1. .1.11... ........ p += 0 * y1
+// 014b000044925400 .......1 .1..1.11 ........ ........ .1...1.. 1..1..1. .1.1.1.. ........ p += 0 * x0 ; y0 = p
+
+// 0000000000805800 ........ ........ ........ ........ ........ 1....... .1.11... ........ p += fp48a * r0b;
+// 0000000003822800 ........ ........ ........ ........ ......11 1.....1. ..1.1... ........ p = f1_2_c1 * r45;
+
+// Important detail: the writes to register (rnn and mnn) seem to be
+// delayed by 2 cycles. That makes the filter computation work out.
+
+
+#include "emu.h"
+#include "swp30d.h"
+
+swp30_disassembler::swp30_disassembler(info *inf) : m_info(inf)
+{
+}
+
+u32 swp30_disassembler::opcode_alignment() const
+{
+ return 1;
+}
+
+std::string swp30_disassembler::gconst(offs_t address) const
+{
+ if(!m_info)
+ return util::string_format("c%03x", address);
+ s16 value = m_info->swp30d_const_r(address);
+ return util::string_format("%g", value / 16384.0);
+}
+
+std::string swp30_disassembler::goffset(offs_t address) const
+{
+ return m_info ? util::string_format("%x", m_info->swp30d_offset_r(address)) : util::string_format("of%02x", address);
+}
+
+u32 swp30_disassembler::b(u64 opc, u32 start, u32 count)
+{
+ return (opc >> start) & ((1 << count) - 1);
+}
+
+void swp30_disassembler::append(std::string &r, const std::string &e)
+{
+ if(r != "")
+ r += " ; ";
+ r += e;
+}
+
+// 33333333 33333333 22222222 22222222 11111111 11111111 00000000 00000000
+// fedcba98 76543210 fedcba98 76543210 fedcba98 76543210 fedcba98 76543210
+
+// 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000
+// 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210
+// XLB----- -rrrrrrr -xlmmmmm m-MM---- -P----** *c---Arr rrrrrsmm mmmm----
+
+// m = low is read port, high is write port, memory register
+// r = low is read port, high is write port, rotating register
+
+// X = used for lo-fi variation only
+// L = lfo read for memory offset
+// * = compute mul + mode
+// A = mul input = m or r
+// s = substract to p instead of adding
+// P = P sent for register write
+// B = register write to mbuf
+// M = memory mode, none/read/write/read+1
+// x = 0 register write to memory, 1 to rotating
+// l = 0 = lfo sent for register write
+
+offs_t swp30_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params)
+{
+ u64 opc = opcodes.r64(pc);
+
+ std::string r;
+
+ if(b(opc, 62, 1))
+ append(r, util::string_format("m%02x = lfo.%02x", b(opc, 4, 6), pc >> 4));
+
+ if(b(opc, 23, 1))
+ switch(b(opc, 24, 2)) {
+ case 0:
+ if(b(opc, 18, 1))
+ append(r, util::string_format("p %c= %s*m%02x", b(opc, 10, 1) ? '-' : '+', gconst(pc), b(opc, 4, 6)));
+ else
+ append(r, util::string_format("p %c= %s*r%02x", b(opc, 10, 1) ? '-' : '+', gconst(pc), b(opc, 11, 7)));
+ break;
+ case 1:
+ append(r, util::string_format("p %c= %s*(r%02x+m%02x)", b(opc, 10, 1) ? '-' : '+', gconst(pc), b(opc, 11, 7), b(opc, 4, 6)));
+ break;
+ case 2:
+ append(r, util::string_format("p = %s*(r%02x+m%02x)", gconst(pc), b(opc, 11, 7), b(opc, 4, 6)));
+ break;
+ case 3:
+ if(b(opc, 18, 1))
+ append(r, util::string_format("p = %s*m%02x", gconst(pc), b(opc, 4, 6)));
+ else
+ append(r, util::string_format("p = %s*r%02x", gconst(pc), b(opc, 11, 7)));
+ break;
+ }
+
+ if(b(opc, 62, 1))
+ append(r, util::string_format("idx = p*4000"));
+
+ if(b(opc, 30, 1) == 1 && b(opc, 61, 1) == 1)
+ append(r, util::string_format("mw = p"));
+
+ if(b(opc, 30, 1) == 1 && b(opc, 61, 1) == 0 && b(opc, 46, 1) == 1 && b(opc, 62, 1) == 0)
+ append(r, util::string_format("m%02x = p", b(opc, 39, 6)));
+
+ if(b(opc, 30, 1) == 1 && b(opc, 61, 1) == 0 && b(opc, 46, 1) == 0)
+ append(r, util::string_format("r%02x = p", b(opc, 48, 7)));
+
+ if(b(opc, 30, 1) == 0 && b(opc, 45, 2) == 2)
+ append(r, util::string_format("m%02x = lfo.%02x", b(opc, 39, 6), pc >> 4));
+
+ if(b(opc, 30, 1) == 0 && b(opc, 45, 2) == 3)
+ append(r, util::string_format("m%02x = m%02x", b(opc, 39, 6), b(opc, 4, 6)));
+
+ if(b(opc, 46, 2) == 2)
+ append(r, util::string_format("m%02x = mr", b(opc, 39, 6)));
+
+ u32 memmode = b(opc, 36, 2);
+ if(memmode) {
+ static const char *modes[4] = { nullptr, "w", "r", "1r" };
+ append(r, util::string_format("mem_%s %x +%s", modes[memmode], b(opc, 33, 3), goffset(pc/3)));
+ }
+
+ stream << r;
+
+ return 1 | SUPPORTED;
+}
diff --git a/src/devices/sound/megd.h b/src/devices/sound/swp30d.h
index 82bad8f4beb..d993f9f03f6 100644
--- a/src/devices/sound/megd.h
+++ b/src/devices/sound/swp30d.h
@@ -1,27 +1,27 @@
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
-// Yamaha MEG - Multiple effects generator
+// Yamaha SWP30 - Multiple effects generator subpart
//
// Audio dsp dedicated to effects generation
//
// Disassembler
-#ifndef DEVICES_SOUND_MEGD_H
-#define DEVICES_SOUND_MEGD_H
+#ifndef DEVICES_SOUND_SWP30D_H
+#define DEVICES_SOUND_SWP30D_H
#pragma once
-class meg_disassembler : public util::disasm_interface
+class swp30_disassembler : public util::disasm_interface
{
public:
class info {
public:
- virtual u16 fp_r(u16 address) const = 0;
- virtual u16 offset_r(u16 address) const = 0;
+ virtual u16 swp30d_const_r(u16 address) const = 0;
+ virtual u16 swp30d_offset_r(u16 address) const = 0;
};
- meg_disassembler(info *inf = nullptr);
+ swp30_disassembler(info *inf = nullptr);
virtual u32 opcode_alignment() const override;
virtual offs_t disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params) override;
@@ -29,7 +29,7 @@ public:
private:
info *m_info;
- std::string gfp(offs_t address) const;
+ std::string gconst(offs_t address) const;
std::string goffset(offs_t address) const;
static inline u32 b(u64 opc, u32 start, u32 count);
diff --git a/src/devices/sound/swx00.cpp b/src/devices/sound/swx00.cpp
new file mode 100644
index 00000000000..5ce1ee79d57
--- /dev/null
+++ b/src/devices/sound/swx00.cpp
@@ -0,0 +1,711 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Yamaha SWX00, rompler/dsp/cpu combo, audio support
+
+#include "emu.h"
+#include "swx00.h"
+
+
+DEFINE_DEVICE_TYPE(SWX00_SOUND, swx00_sound_device, "swx00_sound", "Yamaha SWX00 (sound subsystem)")
+
+// Some tables we need. Lifted from the swp00, probably incorrect.
+
+const std::array<s32, 0x80> swx00_sound_device::attack_linear_step = {
+ 0x00027, 0x0002b, 0x0002f, 0x00033, 0x00037, 0x0003d, 0x00042, 0x00048,
+ 0x0004d, 0x00056, 0x0005e, 0x00066, 0x0006f, 0x0007a, 0x00085, 0x00090,
+ 0x0009b, 0x000ac, 0x000bd, 0x000cc, 0x000de, 0x000f4, 0x00109, 0x00120,
+ 0x00135, 0x00158, 0x00179, 0x00199, 0x001bc, 0x001e7, 0x00214, 0x00240,
+ 0x0026b, 0x002af, 0x002f2, 0x00332, 0x00377, 0x003d0, 0x0042c, 0x00480,
+ 0x004dc, 0x0055e, 0x005e9, 0x0066e, 0x006f4, 0x007a4, 0x00857, 0x0090b,
+ 0x009c3, 0x00acb, 0x00bd6, 0x00ce6, 0x00e00, 0x00f5e, 0x010d2, 0x01234,
+ 0x0139e, 0x015d0, 0x017f3, 0x01a20, 0x01c4a, 0x01f52, 0x02232, 0x0250f,
+ 0x027ff, 0x02c72, 0x03109, 0x0338b, 0x039c4, 0x04038, 0x04648, 0x04c84,
+ 0x05262, 0x05c1c, 0x065af, 0x06f5c, 0x07895, 0x0866f, 0x09470, 0x0a19e,
+ 0x0ae4c, 0x0c566, 0x0db8d, 0x0f00f, 0x10625, 0x12937, 0x14954, 0x16c17,
+ 0x1886e, 0x1c71c, 0x20000, 0x239e1, 0x2647c, 0x2aaab, 0x2ecfc, 0x3241f,
+ 0x35e51, 0x3a83b, 0x40000, 0x4325c, 0x47dc1, 0x4c8f9, 0x50505, 0x55555,
+ 0x58160, 0x5d174, 0x60606, 0x62b2e, 0x67b24, 0x6a63c, 0x6d3a0, 0x6eb3e,
+ 0x71c72, 0x73616, 0x75075, 0x76b98, 0x78788, 0x78788, 0x7a44c, 0x7a44c,
+ 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c, 0x7a44c,
+};
+
+const std::array<s32, 0x20> swx00_sound_device::decay_linear_step = {
+ 0x15083, 0x17ad2, 0x1a41a, 0x1cbe7, 0x1f16d, 0x22ef1, 0x26a44, 0x2a1e4,
+ 0x2da35, 0x34034, 0x3a197, 0x40000, 0x45b82, 0x4b809, 0x51833, 0x57262,
+ 0x5d9f7, 0x6483f, 0x6b15c, 0x71c72, 0x77976, 0x7d119, 0x83127, 0x88889,
+ 0x8d3dd, 0x939a8, 0x991f2, 0x9d89e, 0xa0a0a, 0xa57eb, 0xa72f0, 0xac769,
+};
+
+const std::array<s32, 16> swx00_sound_device::panmap = {
+ 0x000, 0x040, 0x080, 0x0c0,
+ 0x100, 0x140, 0x180, 0x1c0,
+ 0x200, 0x240, 0x280, 0x2c0,
+ 0x300, 0x340, 0x380, 0xfff
+};
+
+swx00_sound_device::swx00_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, SWX00_SOUND, tag, owner, clock),
+ device_sound_interface(mconfig, *this),
+ device_rom_interface(mconfig, *this)
+{
+}
+
+void swx00_sound_device::device_add_mconfig(machine_config &config)
+{
+}
+
+void swx00_sound_device::device_start()
+{
+ m_stream = stream_alloc(0, 2, 44100);
+
+ save_item(NAME(m_sample_start));
+ save_item(NAME(m_sample_end));
+ save_item(NAME(m_sample_address));
+
+ save_item(NAME(m_glo_pan));
+ save_item(NAME(m_rev_dry));
+ save_item(NAME(m_cho_var));
+
+ save_item(NAME(m_attack));
+ save_item(NAME(m_decay));
+
+ save_item(NAME(m_keyon));
+ save_item(NAME(m_state_sel));
+
+ save_item(NAME(m_dsp_offsets));
+ save_item(NAME(m_dsp_values));
+
+ save_item(NAME(m_rom_address));
+ save_item(NAME(m_rom_read_status));
+
+ save_item(NAME(m_sample_pos));
+ save_item(NAME(m_envelope_level));
+
+ save_item(NAME(m_glo_level_cur));
+ save_item(NAME(m_pan_l));
+ save_item(NAME(m_pan_r));
+
+ save_item(NAME(m_active));
+ save_item(NAME(m_decay_on));
+ save_item(NAME(m_decay_done));
+
+ save_item(NAME(m_dpcm_current));
+ save_item(NAME(m_dpcm_next));
+ save_item(NAME(m_dpcm_address));
+
+
+ for(int i=0; i != 128; i++) {
+ u32 v = 0;
+ switch(i >> 3) {
+ default: v = ((i & 7) + 8) << (1 + (i >> 3)); break;
+ case 0xb: v = ((i & 7) + 4) << 13; break;
+ case 0xc: v = ((i & 6) + 6) << 14; break;
+ case 0xd: v = ((i & 4) + 7) << 15; break;
+ case 0xe: v = 15 << 15; break;
+ case 0xf: v = 31 << 15; break;
+ }
+ m_global_step[i] = v;
+ }
+
+ // Delta-packed samples decompression.
+
+ for(int i=0; i<128; i++) {
+ s16 base = ((i & 0x1f) << (5+(i >> 5))) + (((1 << (i >> 5))-1) << 10);
+ m_dpcm[i | 0x80] = - base;
+ m_dpcm[i] = + base;
+ }
+}
+
+void swx00_sound_device::device_reset()
+{
+ std::fill(m_sample_start.begin(), m_sample_start.end(), 0);
+ std::fill(m_sample_end.begin(), m_sample_end.end(), 0);
+ std::fill(m_sample_address.begin(), m_sample_address.end(), 0);
+
+ std::fill(m_glo_pan.begin(), m_glo_pan.end(), 0);
+ std::fill(m_rev_dry.begin(), m_rev_dry.end(), 0);
+ std::fill(m_cho_var.begin(), m_cho_var.end(), 0);
+
+ std::fill(m_attack.begin(), m_attack.end(), 0);
+ std::fill(m_decay.begin(), m_decay.end(), 0);
+
+ std::fill(m_dsp_offsets.begin(), m_dsp_offsets.end(), 0);
+ std::fill(m_dsp_values.begin(), m_dsp_values.end(), 0);
+
+ std::fill(m_sample_pos.begin(), m_sample_pos.end(), 0);
+ std::fill(m_envelope_level.begin(), m_envelope_level.end(), 0);
+
+ std::fill(m_glo_level_cur.begin(), m_glo_level_cur.end(), 0);
+ std::fill(m_pan_l.begin(), m_pan_l.end(), 0);
+ std::fill(m_pan_r.begin(), m_pan_r.end(), 0);
+
+ std::fill(m_active.begin(), m_active.end(), false);
+ std::fill(m_decay_on.begin(), m_decay_on.end(), false);
+ std::fill(m_decay_done.begin(), m_decay_done.end(), false);
+
+ std::fill(m_dpcm_current.begin(), m_dpcm_current.end(), false);
+ std::fill(m_dpcm_next.begin(), m_dpcm_next.end(), false);
+ std::fill(m_dpcm_address.begin(), m_dpcm_address.end(), false);
+
+ m_keyon = 0;
+ m_state_sel = 0;
+ m_rom_address = 0;
+ m_rom_read_status = 0;
+}
+
+void swx00_sound_device::map(address_map &map)
+{
+ map(0x000, 0x7ff).rw(FUNC(swx00_sound_device::snd_r), FUNC(swx00_sound_device::snd_w));
+
+ map(0x001, 0x001).rw(FUNC(swx00_sound_device::state_r), FUNC(swx00_sound_device::state_sel_w));
+
+ map(0x008, 0x00b).w(FUNC(swx00_sound_device::keyon_w));
+ map(0x00c, 0x00c).w(FUNC(swx00_sound_device::keyon_commit_w));
+
+ map(0x110, 0x110).w(FUNC(swx00_sound_device::dsp_offh_w));
+ map(0x120, 0x120).w(FUNC(swx00_sound_device::dsp_valh_w));
+
+ rchan(map, 0x05).rw(FUNC(swx00_sound_device::sample_start_r), FUNC(swx00_sound_device::sample_start_w));
+
+ map(0x180, 0x1ff).w(FUNC(swx00_sound_device::dsp_offl_w));
+ map(0x200, 0x31f).w(FUNC(swx00_sound_device::dsp_vall_w)); // 06-0b
+
+ rchan(map, 0x13).rw(FUNC(swx00_sound_device::glo_pan_r), FUNC(swx00_sound_device::glo_pan_w));
+ rchan(map, 0x14).rw(FUNC(swx00_sound_device::attack_r), FUNC(swx00_sound_device::attack_w));
+ rchan(map, 0x15).rw(FUNC(swx00_sound_device::decay_r), FUNC(swx00_sound_device::decay_w));
+ rchan(map, 0x16).rw(FUNC(swx00_sound_device::rev_dry_r), FUNC(swx00_sound_device::rev_dry_w));
+ rchan(map, 0x17).rw(FUNC(swx00_sound_device::cho_var_r), FUNC(swx00_sound_device::cho_var_w));
+ rchan(map, 0x18).rw(FUNC(swx00_sound_device::sample_address_h_r), FUNC(swx00_sound_device::sample_address_h_w));
+ rchan(map, 0x19).rw(FUNC(swx00_sound_device::sample_address_l_r), FUNC(swx00_sound_device::sample_address_l_w));
+ rchan(map, 0x1a).rw(FUNC(swx00_sound_device::sample_pitch_r), FUNC(swx00_sound_device::sample_pitch_w));
+ rchan(map, 0x1b).rw(FUNC(swx00_sound_device::sample_end_r), FUNC(swx00_sound_device::sample_end_w));
+
+ map(0x808, 0x809).w(FUNC(swx00_sound_device::rom_read_adrh_w));
+ map(0x80a, 0x80b).w(FUNC(swx00_sound_device::rom_read_adrl_w));
+ map(0x80c, 0x80d).r(FUNC(swx00_sound_device::rom_read_status_r));
+ map(0x80e, 0x80f).rw(FUNC(swx00_sound_device::rom_read_r), FUNC(swx00_sound_device::rom_read_w));
+}
+
+u16 swx00_sound_device::snd_r(offs_t offset)
+{
+ u32 chan = offset & 0x1f;
+ u32 slot = offset >> 5;
+ logerror("snd_r %03x %02x.%02x\n", offset*2, chan, slot);
+ return 0;
+}
+
+void swx00_sound_device::snd_w(offs_t offset, u16 data, u16 mem_mask)
+{
+ u32 chan = offset & 0x1f;
+ u32 slot = offset >> 5;
+ if(slot == 0x10 || slot == 0x11)
+ return;
+ logerror("snd_w %03x %02x.%02x %04x @ %04x\n", offset*2, chan, slot, data, mem_mask);
+}
+
+void swx00_sound_device::rom_read_adrh_w(offs_t, u16 data, u16 mem_mask)
+{
+ m_rom_address = (m_rom_address & ~(mem_mask << 16)) | ((data & mem_mask) << 16);
+}
+
+void swx00_sound_device::rom_read_adrl_w(offs_t, u16 data, u16 mem_mask)
+{
+ m_rom_address = (m_rom_address & ~mem_mask) | (data & mem_mask);
+ m_rom_read_status = 1;
+}
+
+u16 swx00_sound_device::rom_read_status_r()
+{
+ return m_rom_read_status;
+}
+
+u16 swx00_sound_device::rom_read_r()
+{
+ m_rom_read_status = 0;
+ return read_word(m_rom_address);
+}
+
+void swx00_sound_device::rom_read_w(u16)
+{
+ m_rom_read_status = 0;
+}
+
+u16 swx00_sound_device::sample_start_r(offs_t chan)
+{
+ return m_sample_start[chan];
+}
+
+void swx00_sound_device::sample_start_w(offs_t chan, u16 data, u16 mem_mask)
+{
+ COMBINE_DATA(&m_sample_start[chan]);
+ if(ACCESSING_BITS_0_7)
+ logerror("sample_start[%02x] = %04x\n", chan, m_sample_start[chan]);
+}
+
+u16 swx00_sound_device::sample_end_r(offs_t chan)
+{
+ return m_sample_end[chan];
+}
+
+void swx00_sound_device::sample_end_w(offs_t chan, u16 data, u16 mem_mask)
+{
+ COMBINE_DATA(&m_sample_end[chan]);
+ if(ACCESSING_BITS_0_7)
+ logerror("sample_end[%02x] = %04x\n", chan, m_sample_end[chan]);
+}
+
+u16 swx00_sound_device::sample_pitch_r(offs_t chan)
+{
+ return m_sample_pitch[chan];
+}
+
+void swx00_sound_device::sample_pitch_w(offs_t chan, u16 data, u16 mem_mask)
+{
+ u16 old = m_sample_pitch[chan];
+ COMBINE_DATA(&m_sample_pitch[chan]);
+ if(ACCESSING_BITS_0_7 && m_sample_pitch[chan] != old)
+ logerror("sample_pitch[%02x] = %04x\n", chan, m_sample_pitch[chan]);
+}
+
+u16 swx00_sound_device::sample_address_h_r(offs_t chan)
+{
+ return m_sample_address[chan] >> 16;
+}
+
+u16 swx00_sound_device::sample_address_l_r(offs_t chan)
+{
+ return m_sample_address[chan];
+}
+
+void swx00_sound_device::sample_address_h_w(offs_t chan, u16 data, u16 mem_mask)
+{
+ m_sample_address[chan] = (m_sample_address[chan] & ~(mem_mask << 16)) | ((data & mem_mask) << 16);
+}
+
+void swx00_sound_device::sample_address_l_w(offs_t chan, u16 data, u16 mem_mask)
+{
+ m_sample_address[chan] = (m_sample_address[chan] & ~mem_mask) | (data & mem_mask);
+ if(ACCESSING_BITS_0_7)
+ logerror("sample_address[%02x] = %08x\n", chan, m_sample_address[chan]);
+}
+
+u16 swx00_sound_device::glo_pan_r(offs_t chan)
+{
+ return m_glo_pan[chan];
+}
+
+void swx00_sound_device::glo_pan_w(offs_t chan, u16 data, u16 mem_mask)
+{
+ COMBINE_DATA(&m_glo_pan[chan]);
+ logerror("%02x: glo=%02x panl=%x panr=%x\n", chan, m_glo_pan[chan] >> 8, (m_glo_pan[chan] >> 4) & 0xf, m_glo_pan[chan] & 0xf);
+}
+
+u16 swx00_sound_device::rev_dry_r(offs_t chan)
+{
+ return m_rev_dry[chan];
+}
+
+void swx00_sound_device::rev_dry_w(offs_t chan, u16 data, u16 mem_mask)
+{
+ COMBINE_DATA(&m_rev_dry[chan]);
+ logerror("%02x: rev=%02x dry=%02x\n", chan, m_rev_dry[chan] >> 8, m_rev_dry[chan] & 0xff);
+}
+
+u16 swx00_sound_device::cho_var_r(offs_t chan)
+{
+ return m_cho_var[chan];
+}
+
+void swx00_sound_device::cho_var_w(offs_t chan, u16 data, u16 mem_mask)
+{
+ COMBINE_DATA(&m_cho_var[chan]);
+ logerror("%02x: cho=%02x var=%02x\n", chan, m_cho_var[chan] >> 8, m_cho_var[chan] & 0xff);
+}
+
+void swx00_sound_device::keyon_w(offs_t offset, u8 data)
+{
+ u32 shift = 24 - 8*offset;
+ m_keyon = (m_keyon & ~(0xff << shift)) | (data << shift);
+}
+
+void swx00_sound_device::keyon_commit_w(u8)
+{
+ m_stream->update();
+ for(int chan = 0; chan != 32; chan++)
+ if(BIT(m_keyon, chan)) {
+ logerror("keyon %02x\n", chan);
+ m_sample_pos[chan] = -m_sample_start[chan] << 15;
+
+ m_sample_pos[chan] = 0;
+
+ m_active[chan] = true;
+ m_decay_on[chan] = false;
+ m_decay_done[chan] = false;
+
+ m_dpcm_current[chan] = 0;
+ m_dpcm_next[chan] = 0;
+ m_dpcm_address[chan] = ((m_sample_address[chan] & 0xffffff) << 1) - m_sample_start[chan];
+
+ m_glo_level_cur[chan] = (m_glo_pan[chan] >> 4) & 0xff0;
+ m_pan_l[chan] = panmap[(m_glo_pan[chan] >> 4) & 15];
+ m_pan_r[chan] = panmap[m_glo_pan[chan] & 15];
+
+ if(m_decay[chan] & 0x8000) {
+ m_envelope_level[chan] = 0;
+ m_decay_on[chan] = true;
+ } else if((m_attack[chan] & 0x8000) || (m_attack[chan] & 0xff))
+ m_envelope_level[chan] = (m_attack[chan] & 0xff) << 20;
+ else
+ m_envelope_level[chan] = 0x8000000;
+ }
+
+ m_keyon = 0;
+}
+
+u16 swx00_sound_device::attack_r(offs_t chan)
+{
+ return m_attack[chan];
+}
+
+void swx00_sound_device::attack_w(offs_t chan, u16 data, u16 mem_mask)
+{
+ COMBINE_DATA(&m_attack[chan]);
+ if(ACCESSING_BITS_0_7)
+ logerror("%02x: attack %02x.%02x\n", chan, m_attack[chan] >> 8, m_attack[chan] & 0xff);
+}
+
+u16 swx00_sound_device::decay_r(offs_t chan)
+{
+ return m_decay[chan];
+}
+
+void swx00_sound_device::decay_w(offs_t chan, u16 data, u16 mem_mask)
+{
+ COMBINE_DATA(&m_decay[chan]);
+ if(ACCESSING_BITS_0_7)
+ logerror("%02x: decay %02x.%02x\n", chan, m_decay[chan] >> 8, m_decay[chan] & 0xff);
+}
+
+void swx00_sound_device::state_sel_w(u8 data)
+{
+ m_state_sel = data;
+}
+
+u8 swx00_sound_device::state_r()
+{
+ int chan = m_state_sel & 0x1f;
+ switch(m_state_sel & 0xe0) {
+ case 0x40: { // Envelope state
+ if(!m_active[chan])
+ return 0xff;
+
+ u8 vol;
+ if(m_decay_on[chan] || (m_attack[chan] & 0x7f) || (m_attack[chan] & 0x8000))
+ vol = m_envelope_level[chan] >> 22;
+ else
+ vol = 0;
+
+ if(m_decay_done[chan])
+ vol |= 0x40;
+ if(m_decay_on[chan])
+ vol |= 0x80;
+
+ return vol;
+ }
+
+ default:
+ logerror("state_r %02x\n", m_state_sel);
+ break;
+ }
+ return 0;
+}
+
+void swx00_sound_device::dsp_valh_w(u8 data)
+{
+ m_dsp_valh = data;
+}
+
+void swx00_sound_device::dsp_vall_w(offs_t reg, u8 data)
+{
+ m_dsp_values[reg] = ((m_dsp_valh << 8) | data) & 0x3ff;
+ // logerror("dsp value[%03x] = %03x\n", reg, m_dsp_values[reg]);
+}
+
+void swx00_sound_device::dsp_offh_w(u8 data)
+{
+ m_dsp_offh = data;
+}
+
+void swx00_sound_device::dsp_offl_w(offs_t reg, u8 data)
+{
+ if(reg & 1) {
+ m_dsp_offsets[reg >> 1] = (m_dsp_offh << 16) | (m_dsp_offsets[reg >> 1] & 0xff00) | data;
+ // logerror("dsp offset[%02x] = %06x\n", reg >> 1, m_dsp_offsets[reg >> 1]);
+ } else
+ m_dsp_offsets[reg >> 1] = (m_dsp_offh << 16) | (data << 8) | (m_dsp_offsets[reg >> 1] & 0xff);
+}
+
+bool swx00_sound_device::istep(s32 &value, s32 limit, s32 step)
+{
+ // fprintf(stderr, "istep(%x, %x, %x)\n", value, limit, step);
+ if(value < limit) {
+ value += step;
+ if(value >= limit) {
+ value = limit;
+ return true;
+ }
+ return false;
+ }
+
+ if(value > limit) {
+ value -= step;
+ if(value <= limit) {
+ value = limit;
+ return true;
+ }
+ return false;
+ }
+
+ return true;
+}
+
+s32 swx00_sound_device::fpadd(s32 value, s32 step)
+{
+ s32 e = value >> 24;
+ s32 m = value & 0xffffff;
+
+ m += step << e;
+ if(m & 0xfe000000)
+ return 0xfffffff;
+
+ while(m & 0x01000000) {
+ m <<= 1;
+ e ++;
+ }
+ if(e >= 16)
+ return 0xfffffff;
+ return (e << 24) | (m & 0xffffff);
+}
+
+s32 swx00_sound_device::fpsub(s32 value, s32 step)
+{
+ s32 e = value >> 24;
+ s32 m = (value & 0xffffff) | 0xfe000000;
+ m = e < 0xc ? m - (step << e) : (m >> (e - 0xb)) - (step << 0xb);
+ if(m >= 0)
+ return 0;
+ if(e >= 0xc)
+ e = 0xb;
+ while(m < 0xfe000000) {
+ if(!e)
+ return 0;
+ e --;
+ m >>= 1;
+ }
+ while(e != 0xf && (m >= 0xff000000)) {
+ e ++;
+ m <<= 1;
+ }
+
+ return (e << 24) | (m & 0xffffff);
+}
+
+bool swx00_sound_device::fpstep(s32 &value, s32 limit, s32 step)
+{
+ // value, limit and step are 4.24 but step has its exponent and
+ // top four bits zero
+
+ if(value == limit)
+ return true;
+ if(value < limit) {
+ value = fpadd(value, step);
+ if(value >= limit) {
+ value = limit;
+ return true;
+ }
+ return false;
+ }
+
+ value = fpsub(value, step);
+ if(value <= limit) {
+ value = limit;
+ return true;
+ }
+ return false;
+}
+
+// sample is signed 24.8
+s32 swx00_sound_device::fpapply(s32 value, s32 sample)
+{
+ if(value >= 0x10000000)
+ return 0;
+ return (s64(sample) - ((s64(sample) * ((value >> 9) & 0x7fff)) >> 16)) >> (value >> 24);
+}
+
+void swx00_sound_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
+{
+ for(int i=0; i != outputs[0].samples(); i++) {
+ s32 dry_l = 0, dry_r = 0;
+ s32 rev = 0;
+ s32 cho_l = 0, cho_r = 0;
+ s32 var_l = 0, var_r = 0;
+
+ for(int chan = 0; chan != 32; chan++) {
+ if(!m_active[chan])
+ continue;
+
+ s16 val0, val1;
+ u32 base_address = m_sample_address[chan] & 0xffffff;
+ s32 spos = m_sample_pos[chan] >> 15;
+ switch(m_sample_address[chan] >> 30) {
+ case 0: { // 16-bits linear
+ offs_t adr = base_address + spos;
+ val0 = read_word(adr);
+ val1 = read_word(adr+1);
+ break;
+ }
+
+ case 1: { // 12-bits linear
+ offs_t adr = base_address + (spos >> 2)*3;
+ switch(spos & 3) {
+ case 0: { // Cabc ..AB .... ....
+ u16 w0 = read_word(adr);
+ u16 w1 = read_word(adr+1);
+ val0 = (w0 & 0x0fff) << 4;
+ val1 = ((w0 & 0xf000) >> 8) | ((w1 & 0x00ff) << 8);
+ break;
+ }
+ case 1: { // c... BCab ...A ....
+ u16 w0 = read_word(adr);
+ u16 w1 = read_word(adr+1);
+ u16 w2 = read_word(adr+2);
+ val0 = ((w0 & 0xf000) >> 8) | ((w1 & 0x00ff) << 8);
+ val1 = ((w1 & 0xff00) >> 4) | ((w2 & 0x000f) << 12);
+ break;
+ }
+ case 2: { // .... bc.. ABCa ....
+ u16 w1 = read_word(adr+1);
+ u16 w2 = read_word(adr+2);
+ val0 = ((w1 & 0xff00) >> 4) | ((w2 & 0x000f) << 12);
+ val1 = w2 & 0xfff0;
+ break;
+ }
+ case 3: { // .... .... abc. .ABC
+ u16 w2 = read_word(adr+2);
+ u16 w3 = read_word(adr+3);
+ val0 = w2 & 0xfff0;
+ val1 = (w3 & 0x0fff) << 4;
+ break;
+ }
+ }
+ break;
+ }
+
+ case 2: { // 8-bits linear
+ offs_t adr = base_address + (spos >> 1);
+ if(spos & 1) {
+ u16 w0 = read_word(adr);
+ u16 w1 = read_word(adr+1);
+ val0 = w0 & 0xff00;
+ val1 = w1 << 8;
+ } else {
+ u16 w0 = read_word(adr);
+ val0 = w0 << 8;
+ val1 = w0 & 0xff00;
+ }
+ break;
+ }
+
+ case 3: { // 8-bits delta-pcm
+ u32 target_address = (base_address << 1) + spos + 1;
+ while(m_dpcm_address[chan] <= target_address) {
+ m_dpcm_current[chan] = m_dpcm_next[chan];
+ u16 idx = read_word(m_dpcm_address[chan] >> 1);
+ if(m_dpcm_address[chan] & 1)
+ idx &= 0xff;
+ else
+ idx >>= 8;
+ s32 sample = m_dpcm_next[chan] + m_dpcm[idx];
+ m_dpcm_address[chan] ++;
+ if(sample < -0x8000)
+ sample = -0x8000;
+ else if(sample > 0x7fff)
+ sample = 0x7fff;
+ m_dpcm_next[chan] = sample;
+ }
+ val0 = m_dpcm_current[chan];
+ val1 = m_dpcm_next[chan];
+ break;
+ }
+ }
+
+ s32 mul = m_sample_pos[chan] & 0x7fff;
+ s32 sample = (val1 * mul + val0 * (0x8000 - mul)) >> 7;
+
+ s32 envelope_level;
+ if(m_decay_on[chan] || (m_attack[chan] & 0xff) || (m_attack[chan] & 0x8000))
+ envelope_level = m_envelope_level[chan];
+ else
+ envelope_level = 0;
+
+ s32 tremolo_level = 0;
+
+ dry_l += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + ((m_rev_dry[chan] << 20) & 0xff00000) + (m_pan_l[chan] << 16), sample);
+ dry_r += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + ((m_rev_dry[chan] << 20) & 0xff00000) + (m_pan_r[chan] << 16), sample);
+ rev += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + ((m_rev_dry[chan] << 12) & 0xff00000), sample);
+ cho_l += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + ((m_cho_var[chan] << 12) & 0xff00000) + (m_pan_l[chan] << 16), sample);
+ cho_r += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + ((m_cho_var[chan] << 12) & 0xff00000) + (m_pan_r[chan] << 16), sample);
+ var_l += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + ((m_cho_var[chan] << 20) & 0xff00000) + (m_pan_l[chan] << 16), sample);
+ var_r += fpapply(envelope_level + (m_glo_level_cur[chan] << 16) + tremolo_level + ((m_cho_var[chan] << 20) & 0xff00000) + (m_pan_r[chan] << 16), sample);
+
+ u32 sample_increment = ((m_sample_pitch[chan] & 0xfff) << (8 + (s16(m_sample_pitch[chan]) >> 12))) >> 4;
+ m_sample_pos[chan] += sample_increment;
+ if((m_sample_pos[chan] >> 15) >= m_sample_end[chan]) {
+ if(!m_sample_end[chan])
+ m_active[chan] = false;
+ else {
+ s32 prev = m_sample_pos[chan];
+ do
+ m_sample_pos[chan] -= (m_sample_end[chan] << 15) | ((m_sample_address[chan] & 0x3f000000) >> (24-9));
+ while((m_sample_pos[chan] >> 15) >= m_sample_end[chan]);
+ m_dpcm_address[chan] += (m_sample_pos[chan] >> 15) - (prev >> 15);
+ }
+ }
+
+ istep(m_glo_level_cur[chan], (m_glo_pan[chan] >> 4) & 0xff0, 1);
+ istep(m_pan_l[chan], panmap[(m_glo_pan[chan] >> 4) & 15], 1);
+ istep(m_pan_r[chan], panmap[m_glo_pan[chan] & 15], 1);
+
+ if(m_decay_on[chan]) {
+ if((m_decay[chan] & 0x6000) == 0x6000)
+ m_decay_done[chan] = fpstep(m_envelope_level[chan], (m_decay[chan] & 0xff) << 20, decay_linear_step[(m_decay[chan] >> 8) & 0x1f]);
+ else
+ m_decay_done[chan] = istep(m_envelope_level[chan], (m_decay[chan] & 0xff) << 20, m_global_step[m_decay[chan] >> 8] << 1);
+ if(m_envelope_level[chan] & 0x8000000)
+ m_active[chan] = false;
+
+ } else if(m_attack[chan] & 0x8000)
+ m_decay_on[chan] = fpstep(m_envelope_level[chan], 0, attack_linear_step[(m_attack[chan] >> 8) & 0x7f]);
+ else
+ m_decay_on[chan] = istep(m_envelope_level[chan], 0, m_global_step[m_attack[chan] >> 8] << 1);
+ }
+
+ dry_l >>= 8;
+ dry_r >>= 8;
+ rev >>= 8;
+ cho_l >>= 8;
+ cho_r >>= 8;
+ var_l >>= 8;
+ var_r >>= 8;
+
+ (void)rev;
+ (void)cho_l;
+ (void)cho_r;
+ (void)var_l;
+ (void)var_r;
+
+ outputs[0].put_int(i, dry_l, 32768);
+ outputs[1].put_int(i, dry_r, 32768);
+ }
+}
diff --git a/src/devices/sound/swx00.h b/src/devices/sound/swx00.h
new file mode 100644
index 00000000000..35a0fb1a52e
--- /dev/null
+++ b/src/devices/sound/swx00.h
@@ -0,0 +1,124 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Yamaha SWX00, rompler/dsp/cpu combo, audio support
+
+#ifndef DEVICES_SOUND_SWX00_H
+#define DEVICES_SOUND_SWX00_H
+
+#pragma once
+
+#include "meg.h"
+#include "dirom.h"
+
+class swx00_sound_device : public device_t, public device_sound_interface, public device_rom_interface<24, 1, -1, ENDIANNESS_BIG>
+{
+public:
+ swx00_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
+
+ void map(address_map &map);
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
+ virtual void device_add_mconfig(machine_config &config) override;
+
+private:
+ sound_stream *m_stream;
+
+ static const std::array<s32, 0x80> attack_linear_step;
+ static const std::array<s32, 0x20> decay_linear_step;
+ static const std::array<s32, 16> panmap;
+ std::array<s32, 0x80> m_global_step;
+ std::array<s16, 0x100> m_dpcm;
+
+ std::array<u16, 0x20> m_sample_start;
+ std::array<u16, 0x20> m_sample_end;
+ std::array<u32, 0x20> m_sample_address;
+ std::array<u16, 0x20> m_sample_pitch;
+
+ std::array<u16, 0x20> m_attack;
+ std::array<u16, 0x20> m_decay;
+
+ std::array<u16, 0x20> m_glo_pan;
+ std::array<u16, 0x20> m_rev_dry;
+ std::array<u16, 0x20> m_cho_var;
+
+ std::array<u32, 0x40> m_dsp_offsets;
+ std::array<u16, 0x120> m_dsp_values;
+
+ std::array<s32, 0x20> m_sample_pos;
+ std::array<s32, 0x20> m_envelope_level;
+ std::array<s32, 0x20> m_glo_level_cur;
+ std::array<s32, 0x20> m_pan_l;
+ std::array<s32, 0x20> m_pan_r;
+ std::array<bool, 0x20> m_active, m_decay_on, m_decay_done;
+ std::array<s16, 0x20> m_dpcm_current;
+ std::array<s16, 0x20> m_dpcm_next;
+ std::array<u32, 0x20> m_dpcm_address;
+
+ u32 m_keyon;
+ u32 m_rom_address;
+ u16 m_rom_read_status;
+ u8 m_state_sel;
+ u8 m_dsp_offh, m_dsp_valh;
+
+ // Generic catch-all
+ u16 snd_r(offs_t offset);
+ void snd_w(offs_t offset, u16 data, u16 mem_mask);
+
+ inline auto &rchan(address_map &map, int idx) {
+ return map(idx*0x40, idx*0x40+1).select(0x3e);
+ }
+
+ u16 sample_start_r(offs_t chan);
+ void sample_start_w(offs_t chan, u16 data, u16 mem_mask);
+ u16 sample_end_r(offs_t chan);
+ void sample_end_w(offs_t chan, u16 data, u16 mem_mask);
+ u16 sample_address_h_r(offs_t chan);
+ u16 sample_address_l_r(offs_t chan);
+ void sample_address_h_w(offs_t chan, u16 data, u16 mem_mask);
+ void sample_address_l_w(offs_t chan, u16 data, u16 mem_mask);
+ u16 sample_pitch_r(offs_t chan);
+ void sample_pitch_w(offs_t chan, u16 data, u16 mem_mask);
+
+ u16 glo_pan_r(offs_t chan);
+ void glo_pan_w(offs_t chan, u16 data, u16 mem_mask);
+ u16 rev_dry_r(offs_t chan);
+ void rev_dry_w(offs_t chan, u16 data, u16 mem_mask);
+ u16 cho_var_r(offs_t chan);
+ void cho_var_w(offs_t chan, u16 data, u16 mem_mask);
+
+ u16 attack_r(offs_t chan);
+ void attack_w(offs_t chan, u16 data, u16 mem_mask);
+ u16 decay_r(offs_t chan);
+ void decay_w(offs_t chan, u16 data, u16 mem_mask);
+
+ void keyon_w(offs_t offset, u8 data);
+ void keyon_commit_w(u8);
+
+ void dsp_offh_w(u8 data);
+ void dsp_offl_w(offs_t reg, u8 data);
+ void dsp_valh_w(u8 data);
+ void dsp_vall_w(offs_t reg, u8 data);
+
+ void rom_read_adrh_w(offs_t, u16 data, u16 mem_mask);
+ void rom_read_adrl_w(offs_t, u16 data, u16 mem_mask);
+ u16 rom_read_status_r();
+ u16 rom_read_r();
+ void rom_read_w(u16);
+
+ void state_sel_w(u8 data);
+ u8 state_r();
+
+ static bool istep(s32 &value, s32 limit, s32 step);
+ static bool fpstep(s32 &value, s32 limit, s32 step);
+ static s32 fpadd(s32 value, s32 step);
+ static s32 fpsub(s32 value, s32 step);
+ static s32 fpapply(s32 value, s32 sample);
+};
+
+DECLARE_DEVICE_TYPE(SWX00_SOUND, swx00_sound_device)
+
+#endif
diff --git a/src/devices/sound/t6721a.cpp b/src/devices/sound/t6721a.cpp
index 2c29e31dc62..a094d4dc783 100644
--- a/src/devices/sound/t6721a.cpp
+++ b/src/devices/sound/t6721a.cpp
@@ -31,14 +31,14 @@ DEFINE_DEVICE_TYPE(T6721A, t6721a_device, "t6721a", "Toshiba T6721A")
// t6721a_device - constructor
//-------------------------------------------------
-t6721a_device::t6721a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, T6721A, tag, owner, clock),
- device_sound_interface(mconfig, *this),
- m_write_eos(*this),
- m_write_phi2(*this),
- m_write_dtrd(*this),
- m_write_apd(*this),
- m_stream(nullptr)
+t6721a_device::t6721a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, T6721A, tag, owner, clock),
+ device_sound_interface(mconfig, *this),
+ m_write_eos(*this),
+ m_write_phi2(*this),
+ m_write_dtrd(*this),
+ m_write_apd(*this),
+ m_stream(nullptr)
{
}
@@ -49,12 +49,6 @@ t6721a_device::t6721a_device(const machine_config &mconfig, const char *tag, dev
void t6721a_device::device_start()
{
- // resolve callbacks
- m_write_eos.resolve_safe();
- m_write_phi2.resolve_safe();
- m_write_dtrd.resolve_safe();
- m_write_apd.resolve_safe();
-
// create sound stream
m_stream = stream_alloc(0, 1, machine().sample_rate());
}
@@ -94,7 +88,7 @@ void t6721a_device::write(uint8_t data)
// di_w - data input write
//-------------------------------------------------
-WRITE_LINE_MEMBER( t6721a_device::di_w )
+void t6721a_device::di_w(int state)
{
}
@@ -103,7 +97,7 @@ WRITE_LINE_MEMBER( t6721a_device::di_w )
// eos_r - eos read
//-------------------------------------------------
-READ_LINE_MEMBER( t6721a_device::eos_r )
+int t6721a_device::eos_r()
{
return 1;
}
diff --git a/src/devices/sound/t6721a.h b/src/devices/sound/t6721a.h
index 52a76393894..52f845f81fb 100644
--- a/src/devices/sound/t6721a.h
+++ b/src/devices/sound/t6721a.h
@@ -56,9 +56,9 @@ public:
uint8_t read();
void write(uint8_t data);
- DECLARE_WRITE_LINE_MEMBER( di_w );
+ void di_w(int state);
- DECLARE_READ_LINE_MEMBER( eos_r );
+ int eos_r();
protected:
// device-level overrides
diff --git a/src/devices/sound/ta7630.cpp b/src/devices/sound/ta7630.cpp
index 6dcb84455b9..7e005eddb4a 100644
--- a/src/devices/sound/ta7630.cpp
+++ b/src/devices/sound/ta7630.cpp
@@ -40,25 +40,21 @@ DEFINE_DEVICE_TYPE(TA7630, ta7630_device, "ta7630", "Toshiba TA7630P")
ta7630_device::ta7630_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, TA7630, tag, owner, clock)
-// ,device_sound_interface(mconfig, *this)
{
}
-
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void ta7630_device::device_start()
{
- int i;
-
double db = 0.0;
double db_step = 1.50; /* 1.50 dB step (at least, maybe more) */
double db_step_inc = 0.125;
- for (i = 0; i < 16; i++)
+ for (int i = 0; i < 16; i++)
{
double max = 100.0 / pow(10.0, db/20.0 );
m_vol_ctrl[15 - i] = max / 100.0;
@@ -72,29 +68,18 @@ void ta7630_device::device_start()
}
-//-------------------------------------------------
-// device_reset - device-specific reset
-//-------------------------------------------------
-
-void ta7630_device::device_reset()
-{
-
-}
-
//**************************************************************************
// filter setters
//**************************************************************************
-void ta7630_device::set_device_volume(device_sound_interface *device,uint8_t value)
+void ta7630_device::set_device_volume(device_sound_interface *device, uint8_t value)
{
- device->set_output_gain(ALL_OUTPUTS,m_vol_ctrl[value & 0xf]);
+ device->set_output_gain(ALL_OUTPUTS, m_vol_ctrl[value & 0xf]);
}
-// TODO: Most Taito implementations uses this, is it correct?
-void ta7630_device::set_channel_volume(device_sound_interface *device, uint8_t ch,uint8_t value)
+// TODO: Most Taito implementations uses this, is it correct?
+void ta7630_device::set_channel_volume(device_sound_interface *device, uint8_t ch, uint8_t value)
{
- device->set_output_gain(ch,m_vol_ctrl[value & 0xf]);
+ device->set_output_gain(ch, m_vol_ctrl[value & 0xf]);
}
-
-
diff --git a/src/devices/sound/ta7630.h b/src/devices/sound/ta7630.h
index e32b4bab407..4851ba696b5 100644
--- a/src/devices/sound/ta7630.h
+++ b/src/devices/sound/ta7630.h
@@ -18,21 +18,18 @@
// ======================> ta7630_device
class ta7630_device : public device_t
- /*, public device_sound_interface*/
{
public:
// construction/destruction
ta7630_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
// filter setters
- void set_device_volume(device_sound_interface *device,uint8_t value);
- void set_channel_volume(device_sound_interface *device, uint8_t ch,uint8_t value);
+ void set_device_volume(device_sound_interface *device, uint8_t value);
+ void set_channel_volume(device_sound_interface *device, uint8_t ch, uint8_t value);
protected:
// device-level overrides
- //virtual void device_validity_check(validity_checker &valid) const override;
virtual void device_start() override;
- virtual void device_reset() override;
private:
double m_vol_ctrl[16]; // table for volume gains
@@ -43,10 +40,4 @@ private:
DECLARE_DEVICE_TYPE(TA7630, ta7630_device)
-
-//**************************************************************************
-// GLOBAL VARIABLES
-//**************************************************************************
-
-
#endif // MAME_SOUND_TA7630_H
diff --git a/src/devices/sound/tms5110.cpp b/src/devices/sound/tms5110.cpp
index a9d3395a4d2..42899a0ac4d 100644
--- a/src/devices/sound/tms5110.cpp
+++ b/src/devices/sound/tms5110.cpp
@@ -149,13 +149,10 @@ static int16_t clip_analog(int16_t cliptemp);
void tms5110_device::new_int_write(uint8_t rc, uint8_t m0, uint8_t m1, uint8_t addr)
{
- if (!m_m0_cb.isnull())
- m_m0_cb(m0);
- if (!m_m1_cb.isnull())
- m_m1_cb(m1);
- if (!m_addr_cb.isnull())
- m_addr_cb((offs_t)0, addr);
- if (!m_romclk_cb.isnull())
+ m_m0_cb(m0);
+ m_m1_cb(m1);
+ m_addr_cb(offs_t(0), addr);
+ if (!m_romclk_cb.isunset())
{
//printf("rc %d\n", rc);
m_romclk_cb(rc);
@@ -176,7 +173,7 @@ uint8_t tms5110_device::new_int_read()
new_int_write(0, 1, 0, 0); // romclk 0, m0 1, m1 0, addr bus nybble = 0/open bus
new_int_write(1, 0, 0, 0); // romclk 1, m0 0, m1 0, addr bus nybble = 0/open bus
new_int_write(0, 0, 0, 0); // romclk 0, m0 0, m1 0, addr bus nybble = 0/open bus
- if (!m_data_cb.isnull())
+ if (!m_data_cb.isunset())
return m_data_cb();
if (DEBUG_5110) logerror("WARNING: CALLBACK MISSING, RETURNING 0!\n");
return 0;
@@ -1040,13 +1037,6 @@ void tms5110_device::device_start()
fatalerror("Unknown variant in tms5110_create\n");
}
- /* resolve lines */
- m_m0_cb.resolve();
- m_m1_cb.resolve();
- m_romclk_cb.resolve();
- m_addr_cb.resolve();
- m_data_cb.resolve();
-
/* initialize a stream */
m_stream = stream_alloc(0, 1, clock() / 80);
@@ -1108,6 +1098,10 @@ void tms5110_device::device_reset()
m_next_is_address = false;
m_address = 0;
m_addr_bit = 0;
+
+ m_romclk_hack_timer->adjust(attotime::never);
+ m_romclk_hack_timer_started = false;
+ m_romclk_hack_state = false;
}
@@ -1351,10 +1345,6 @@ TIMER_CALLBACK_MEMBER(tmsprom_device::update_romclk)
void tmsprom_device::device_start()
{
- /* resolve lines */
- m_pdc_cb.resolve_safe();
- m_ctl_cb.resolve_safe();
-
m_romclk_timer = timer_alloc(FUNC(tmsprom_device::update_romclk), this);
m_romclk_timer->adjust(attotime::zero, 0, attotime::from_hz(clock()));
@@ -1446,7 +1436,7 @@ tms5110_device::tms5110_device(const machine_config &mconfig, device_type type,
, m_m0_cb(*this)
, m_m1_cb(*this)
, m_addr_cb(*this)
- , m_data_cb(*this)
+ , m_data_cb(*this, 0)
, m_romclk_cb(*this)
{
}
diff --git a/src/devices/sound/tms5220.cpp b/src/devices/sound/tms5220.cpp
index 77622b12fef..77a7faeca76 100644
--- a/src/devices/sound/tms5220.cpp
+++ b/src/devices/sound/tms5220.cpp
@@ -368,37 +368,35 @@ emulating the tms5220 in MCU code). Look for a 16-pin chip at U6 labeled
/* *****debugging defines***** */
-// general, somewhat obsolete, catch all for debugs which don't fit elsewhere
-#define LOG_GENERAL (1 << 0)
-/* 5220 only; above dumps the data written to the tms52xx to stdout, useful
+/* 5220 only; above dumps the data written to the tms52xx to the error log, useful
for making logged data dumps for real hardware tests */
-#define LOG_DUMP_INPUT_DATA (1 << 1)
+#define LOG_DUMP_INPUT_DATA (1U << 1)
// 5220 only; above debugs FIFO stuff: writes, reads and flag updates
-#define LOG_FIFO (1 << 2)
+#define LOG_FIFO (1U << 2)
// dumps each speech frame as binary
-#define LOG_PARSE_FRAME_DUMP_BIN (1 << 3)
+#define LOG_PARSE_FRAME_DUMP_BIN (1U << 3)
// dumps each speech frame as hex
-#define LOG_PARSE_FRAME_DUMP_HEX (1 << 4)
+#define LOG_PARSE_FRAME_DUMP_HEX (1U << 4)
// dumps info if a frame ran out of data
-#define LOG_FRAME_ERRORS (1 << 6)
+#define LOG_FRAME_ERRORS (1U << 6)
// dumps all non-speech-data command writes
-#define LOG_COMMAND_DUMP (1 << 7)
+#define LOG_COMMAND_DUMP (1U << 7)
// dumps decoded info about command writes
-#define LOG_COMMAND_VERBOSE (1 << 8)
-// spams the errorlog with i/o ready messages whenever the ready or irq pin is read
-#define LOG_PIN_READS (1 << 9)
+#define LOG_COMMAND_VERBOSE (1U << 8)
+// spams the error log with i/o ready messages whenever the ready or irq pin is read
+#define LOG_PIN_READS (1U << 9)
// dumps debug information related to the sample generation loop, i.e. whether interpolation is inhibited or not, and what the current and target values for each frame are.
-#define LOG_GENERATION (1 << 10)
+#define LOG_GENERATION (1U << 10)
// dumps MUCH MORE debug information related to the sample generation loop, namely the excitation, energy, pitch, k*, and output values for EVERY SINGLE SAMPLE during a frame.
-#define LOG_GENERATION_VERBOSE (1 << 11)
+#define LOG_GENERATION_VERBOSE (1U << 11)
// dumps the lattice filter state data each sample.
-#define LOG_LATTICE (1 << 12)
-// dumps info to stderr whenever the analog clip hardware is (or would be) clipping the signal.
-#define LOG_CLIP (1 << 13)
+#define LOG_LATTICE (1U << 12)
+// dumps info to the error log whenever the analog clip hardware is (or would be) clipping the signal.
+#define LOG_CLIP (1U << 13)
// debugs the io ready callback timer
-#define LOG_IO_READY (1 << 14)
+#define LOG_IO_READY (1U << 14)
// debugs the tms5220_data_r and data_w access methods which actually respect rs and ws
-#define LOG_RS_WS (1 << 15)
+#define LOG_RS_WS (1U << 15)
//#define VERBOSE (LOG_GENERAL | LOG_DUMP_INPUT_DATA | LOG_FIFO | LOG_PARSE_FRAME_DUMP_HEX | LOG_FRAME_ERRORS | LOG_COMMAND_DUMP | LOG_COMMAND_VERBOSE | LOG_PIN_READS | LOG_GENERATION | LOG_GENERATION_VERBOSE | LOG_LATTICE | LOG_CLIP | LOG_IO_READY | LOG_RS_WS)
#include "logmacro.h"
@@ -570,13 +568,10 @@ void tms5220_device::printbits(long data, int num)
***********************************************************************************************/
void tms5220_device::new_int_write(uint8_t rc, uint8_t m0, uint8_t m1, uint8_t addr)
{
- if (!m_m0_cb.isnull())
- m_m0_cb(m0);
- if (!m_m1_cb.isnull())
- m_m1_cb(m1);
- if (!m_addr_cb.isnull())
- m_addr_cb((offs_t)0, addr);
- if (!m_romclk_cb.isnull())
+ m_m0_cb(m0);
+ m_m1_cb(m1);
+ m_addr_cb(offs_t(0), addr);
+ if (!m_romclk_cb.isunset())
{
//printf("rc %d\n", rc);
m_romclk_cb(rc);
@@ -607,9 +602,9 @@ uint8_t tms5220_device::new_int_read()
new_int_write(0, 1, 0, 0); // romclk 0, m0 1, m1 0, addr bus nybble = 0/open bus
new_int_write(1, 0, 0, 0); // romclk 1, m0 0, m1 0, addr bus nybble = 0/open bus
new_int_write(0, 0, 0, 0); // romclk 0, m0 0, m1 0, addr bus nybble = 0/open bus
- if (!m_data_cb.isnull())
+ if (!m_data_cb.isunset())
return m_data_cb();
- LOGMASKED(LOG_GENERAL, "WARNING: CALLBACK MISSING, RETURNING 0!\n");
+ LOG("WARNING: CALLBACK MISSING, RETURNING 0!\n");
return 0;
}
@@ -741,7 +736,7 @@ void tms5220_device::update_fifo_status_and_ints()
// also, in this case, regardless if DDIS was set, unset it.
if (m_previous_talk_status && !talk_status())
{
- LOGMASKED(LOG_GENERAL, "Talk status WAS 1, is now 0, unsetting DDIS and firing an interrupt!\n");
+ LOG("Talk status WAS 1, is now 0, unsetting DDIS and firing an interrupt!\n");
set_interrupt_state(1);
m_DDIS = false;
}
@@ -789,7 +784,7 @@ int tms5220_device::extract_bits(int count)
while (count--)
{
val = (val << 1) | new_int_read();
- LOGMASKED(LOG_GENERAL, "bit read: %d\n", val&1);
+ LOG("bit read: %d\n", val&1);
}
#endif
}
@@ -802,7 +797,7 @@ void tms5220_device::perform_dummy_read()
if (m_schedule_dummy_read)
{
int data = new_int_read();
- LOGMASKED(LOG_GENERAL, "TMS5110 performing dummy read; value read = %1i\n", data & 1);
+ LOG("TMS5110 performing dummy read; value read = %1i\n", data & 1);
m_schedule_dummy_read = false;
}
}
@@ -886,7 +881,7 @@ void tms5220_device::process(int16_t *buffer, unsigned int size)
int i, bitout;
int32_t this_sample;
- LOGMASKED(LOG_GENERAL, "process called with size of %d; IP=%d, PC=%d, subcycle=%d, m_SPEN=%d, m_TALK=%d, m_TALKD=%d\n", size, m_IP, m_PC, m_subcycle, m_SPEN, m_TALK, m_TALKD);
+ LOG("process called with size of %d; IP=%d, PC=%d, subcycle=%d, m_SPEN=%d, m_TALK=%d, m_TALKD=%d\n", size, m_IP, m_PC, m_subcycle, m_SPEN, m_TALK, m_TALKD);
/* loop until the buffer is full or we've stopped speaking */
while (size > 0)
@@ -1219,8 +1214,8 @@ int32_t tms5220_device::matrix_multiply(int32_t a, int32_t b) const
while (b>16383) { b-=32768; }
while (b<-16384) { b+=32768; }
result = ((a*b)>>9); /** TODO: this isn't technically right to the chip, which truncates the lowest result bit, but it causes glitches otherwise. **/
- if (result>16383) LOGMASKED(LOG_GENERAL, "matrix multiplier overflowed! a: %x, b: %x, result: %x", a, b, result);
- if (result<-16384) LOGMASKED(LOG_GENERAL, "matrix multiplier underflowed! a: %x, b: %x, result: %x", a, b, result);
+ if (result>16383) LOG("matrix multiplier overflowed! a: %x, b: %x, result: %x", a, b, result);
+ if (result<-16384) LOG("matrix multiplier underflowed! a: %x, b: %x, result: %x", a, b, result);
return result;
}
@@ -1542,9 +1537,9 @@ void tms5220_device::parse_frame()
LOGMASKED(LOG_PARSE_FRAME_DUMP_BIN | LOG_PARSE_FRAME_DUMP_HEX, "\n");
if (m_DDIS)
- LOGMASKED(LOG_GENERAL, "Parsed a frame successfully in FIFO - %d bits remaining\n", (m_fifo_count*8)-(m_fifo_bits_taken));
+ LOG("Parsed a frame successfully in FIFO - %d bits remaining\n", (m_fifo_count*8)-(m_fifo_bits_taken));
else
- LOGMASKED(LOG_GENERAL, "Parsed a frame successfully in ROM\n");
+ LOG("Parsed a frame successfully in ROM\n");
return;
ranout:
@@ -1583,8 +1578,7 @@ void tms5220_device::update_ready_state()
if (m_ready_pin != state)
{
LOGMASKED(LOG_PIN_READS, "ready pin set to state %d\n", state);
- if (!m_readyq_handler.isnull())
- m_readyq_handler(!state);
+ m_readyq_handler(!state);
m_ready_pin = state;
}
}
@@ -1641,15 +1635,6 @@ void tms5220_device::device_start()
fatalerror("Unknown variant in tms5220_set_variant\n");
}
- /* resolve callbacks */
- m_irq_handler.resolve_safe();
- m_readyq_handler.resolve();
- m_m0_cb.resolve();
- m_m1_cb.resolve();
- m_romclk_cb.resolve();
- m_addr_cb.resolve();
- m_data_cb.resolve();
-
/* initialize a stream */
m_stream = stream_alloc(0, 1, clock() / 80);
@@ -1726,7 +1711,6 @@ void tms5220_device::device_reset()
m_next_is_address = false;
m_addr_bit = 0;
m_CTL_buffer = 0;
-
}
TIMER_CALLBACK_MEMBER(tms5220_device::set_io_ready)
@@ -1777,7 +1761,7 @@ TIMER_CALLBACK_MEMBER(tms5220_device::set_io_ready)
/*
* /RS line write handler
*/
-WRITE_LINE_MEMBER( tms5220_device::rsq_w )
+void tms5220_device::rsq_w(int state)
{
m_true_timing = true;
state &= 0x01;
@@ -1824,7 +1808,7 @@ WRITE_LINE_MEMBER( tms5220_device::rsq_w )
/*
* /WS line write handler
*/
-WRITE_LINE_MEMBER( tms5220_device::wsq_w )
+void tms5220_device::wsq_w(int state)
{
m_true_timing = true;
state &= 0x01;
@@ -1843,7 +1827,7 @@ WRITE_LINE_MEMBER( tms5220_device::wsq_w )
LOGMASKED(LOG_RS_WS, "tms5220_wsq_w: illegal\n");
return;
}
- else if ( new_val == 3)
+ else if (new_val == 3)
{
/* high impedance */
m_read_latch = 0xff;
@@ -2011,7 +1995,7 @@ uint8_t tms5220_device::status_r()
***********************************************************************************************/
-READ_LINE_MEMBER( tms5220_device::readyq_r )
+int tms5220_device::readyq_r()
{
// prevent debugger from changing the internal state
if (!machine().side_effects_disabled())
@@ -2027,7 +2011,7 @@ READ_LINE_MEMBER( tms5220_device::readyq_r )
***********************************************************************************************/
-READ_LINE_MEMBER( tms5220_device::intq_r )
+int tms5220_device::intq_r()
{
// prevent debugger from changing the internal state
if (!machine().side_effects_disabled())
@@ -2106,7 +2090,7 @@ tms5220_device::tms5220_device(const machine_config &mconfig, device_type type,
, m_m0_cb(*this)
, m_m1_cb(*this)
, m_addr_cb(*this)
- , m_data_cb(*this)
+ , m_data_cb(*this, 0)
, m_romclk_cb(*this)
{
}
diff --git a/src/devices/sound/tms5220.h b/src/devices/sound/tms5220.h
index 82a589c486d..061df7b0266 100644
--- a/src/devices/sound/tms5220.h
+++ b/src/devices/sound/tms5220.h
@@ -45,8 +45,8 @@ public:
// all lines with suffix q are active low!
- WRITE_LINE_MEMBER( rsq_w );
- WRITE_LINE_MEMBER( wsq_w );
+ void rsq_w(int state);
+ void wsq_w(int state);
void combined_rsq_wsq_w(u8 data);
/* this combined_rsq_wsq_w hack is necessary for specific systems such as
@@ -61,8 +61,8 @@ public:
void data_w(uint8_t data);
uint8_t status_r();
- READ_LINE_MEMBER( readyq_r );
- READ_LINE_MEMBER( intq_r );
+ int readyq_r();
+ int intq_r();
protected:
tms5220_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, int variant);
diff --git a/src/devices/sound/tt5665.cpp b/src/devices/sound/tt5665.cpp
index 2fd7c4f0e85..363481669ff 100644
--- a/src/devices/sound/tt5665.cpp
+++ b/src/devices/sound/tt5665.cpp
@@ -251,10 +251,11 @@ void tt5665_device::sound_stream_update(sound_stream &stream, std::vector<read_s
//-------------------------------------------------
-// rom_bank_updated - the rom bank has changed
+// rom_bank_pre_change - refresh the stream if the
+// ROM banking changes
//-------------------------------------------------
-void tt5665_device::rom_bank_updated()
+void tt5665_device::rom_bank_pre_change()
{
m_stream->update();
}
diff --git a/src/devices/sound/tt5665.h b/src/devices/sound/tt5665.h
index b0c6669009a..5d3f0a155bc 100644
--- a/src/devices/sound/tt5665.h
+++ b/src/devices/sound/tt5665.h
@@ -72,7 +72,7 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
// device_rom_interface overrides
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
private:
// a single voice
diff --git a/src/devices/sound/uda1344.cpp b/src/devices/sound/uda1344.cpp
index 183ba51fd36..b8d445ef3b8 100644
--- a/src/devices/sound/uda1344.cpp
+++ b/src/devices/sound/uda1344.cpp
@@ -7,13 +7,13 @@
****************************************************************************/
#include "emu.h"
-#include "sound/uda1344.h"
+#include "uda1344.h"
-#define LOG_ADDR (1 << 1)
-#define LOG_STATUS_REG (1 << 2)
-#define LOG_DATA_REG (1 << 3)
-#define LOG_INPUT (1 << 4)
-#define LOG_OVERRUNS (1 << 5)
+#define LOG_ADDR (1U << 1)
+#define LOG_STATUS_REG (1U << 2)
+#define LOG_DATA_REG (1U << 3)
+#define LOG_INPUT (1U << 4)
+#define LOG_OVERRUNS (1U << 5)
#define LOG_ALL (LOG_ADDR | LOG_STATUS_REG | LOG_DATA_REG | LOG_INPUT | LOG_OVERRUNS)
#define VERBOSE (0)
@@ -47,6 +47,9 @@ void uda1344_device::device_start()
{
m_stream = stream_alloc(0, 2, BASE_FREQUENCY);
+ m_buffer[0].resize(BUFFER_SIZE);
+ m_buffer[1].resize(BUFFER_SIZE);
+
save_item(NAME(m_buffer[0]));
save_item(NAME(m_buffer[1]));
save_item(NAME(m_bufin));
@@ -63,11 +66,6 @@ void uda1344_device::device_start()
save_item(NAME(m_power_reg));
save_item(NAME(m_dac_enable));
save_item(NAME(m_adc_enable));
-
- m_l3_ack_out.resolve_safe();
-
- m_buffer[0].resize(BUFFER_SIZE);
- m_buffer[1].resize(BUFFER_SIZE);
}
void uda1344_device::device_reset()
diff --git a/src/devices/sound/upd1771.cpp b/src/devices/sound/upd1771.cpp
index 4e865ea2c42..6b0ef06b884 100644
--- a/src/devices/sound/upd1771.cpp
+++ b/src/devices/sound/upd1771.cpp
@@ -250,9 +250,6 @@ upd1771c_device::upd1771c_device(const machine_config &mconfig, const char *tag,
void upd1771c_device::device_start()
{
- /* resolve callbacks */
- m_ack_handler.resolve_safe();
-
m_timer = timer_alloc(FUNC(upd1771c_device::ack_callback), this);
m_channel = stream_alloc(0, 1, clock() / 4);
diff --git a/src/devices/sound/upd7752.cpp b/src/devices/sound/upd7752.cpp
index 7c063fbee95..8f8a3a6f9a3 100644
--- a/src/devices/sound/upd7752.cpp
+++ b/src/devices/sound/upd7752.cpp
@@ -9,8 +9,7 @@ skeleton device
***************************************************************************/
#include "emu.h"
-#include "sound/upd7752.h"
-
+#include "upd7752.h"
/* status flags */
diff --git a/src/devices/sound/upd7759.cpp b/src/devices/sound/upd7759.cpp
index 2da49639c82..1579fd8ee39 100644
--- a/src/devices/sound/upd7759.cpp
+++ b/src/devices/sound/upd7759.cpp
@@ -257,7 +257,7 @@ void upd775x_device::device_clock_changed()
m_channel->set_sample_rate(clock() / 4);
}
-void upd775x_device::rom_bank_updated()
+void upd775x_device::rom_bank_pre_change()
{
m_channel->update();
}
@@ -269,8 +269,6 @@ void upd7759_device::device_start()
m_sample_offset_shift = 1;
m_timer = timer_alloc(FUNC(upd7759_device::drq_update), this);
-
- m_drqcallback.resolve_safe();
}
@@ -281,7 +279,6 @@ void upd7759_device::device_start()
void upd775x_device::device_reset()
{
m_pos = 0;
- //m_fifo_in = 0; // this seems to keep state when /RESET line asserted (test case: konmedal.cpp games)
m_state = STATE_IDLE;
m_clocks_left = 0;
m_nibbles_left = 0;
@@ -565,18 +562,13 @@ void upd775x_device::advance_state()
}
}
-TIMER_CALLBACK_MEMBER(upd775x_device::sync_port_write)
-{
- m_fifo_in = param;
-}
-
TIMER_CALLBACK_MEMBER(upd7759_device::drq_update)
{
+ m_channel->update();
+
uint8_t olddrq = m_drq;
int old_state = m_state;
- m_channel->update();
-
advance_state();
LOG_STATE("upd7759_slave_update: DRQ %d->%d\n", olddrq, m_drq);
@@ -590,105 +582,79 @@ TIMER_CALLBACK_MEMBER(upd7759_device::drq_update)
m_timer->adjust(m_clock_period * m_clocks_left);
}
+
+
/************************************************************
I/O handlers
*************************************************************/
-WRITE_LINE_MEMBER( upd775x_device::reset_w )
+void upd775x_device::reset_w(int state)
{
machine().scheduler().synchronize(timer_expired_delegate(FUNC(upd775x_device::internal_reset_w), this), state);
}
TIMER_CALLBACK_MEMBER(upd775x_device::internal_reset_w)
{
+ m_channel->update();
+
uint8_t oldreset = m_reset;
m_reset = (param != 0);
- m_channel->update();
-
if (oldreset && !m_reset)
device_reset();
}
-TIMER_CALLBACK_MEMBER(upd7759_device::internal_reset_w)
-{
- uint8_t oldreset = m_reset;
- upd775x_device::internal_reset_w(param);
-
- if (!oldreset && m_reset)
- {
- if (!m_md)
- {
- m_mode = MODE_SLAVE;
- m_state = STATE_START;
- m_timer->adjust(attotime::zero);
- }
- }
-}
-WRITE_LINE_MEMBER( upd775x_device::start_w )
+void upd775x_device::start_w(int state)
{
machine().scheduler().synchronize(timer_expired_delegate(FUNC(upd775x_device::internal_start_w), this), state);
}
-void upd7759_device::internal_start_w(int state)
+void upd775x_device::internal_start_w(int state)
{
+ m_channel->update();
+
uint8_t oldstart = m_start;
m_start = (state != 0);
LOG_STATE("upd7759_start_w: %d->%d\n", oldstart, m_start);
- m_channel->update();
-
if (m_state == STATE_IDLE && m_mode == MODE_STAND_ALONE && oldstart && !m_start && m_reset)
{
m_state = STATE_START;
-
- if (m_mode == MODE_SLAVE)
- m_timer->adjust(attotime::zero);
}
}
-void upd7756_device::internal_start_w(int state)
-{
-
- uint8_t oldstart = m_start;
- m_start = (state != 0);
- LOG_STATE("upd7759_start_w: %d->%d\n", oldstart, m_start);
+void upd775x_device::port_w(u8 data)
+{
+ machine().scheduler().synchronize(timer_expired_delegate(FUNC(upd775x_device::internal_port_w), this), data);
+}
+TIMER_CALLBACK_MEMBER(upd775x_device::internal_port_w)
+{
m_channel->update();
- if (m_state == STATE_IDLE && oldstart && !m_start && m_reset)
- {
- m_state = STATE_START;
- }
+ m_fifo_in = param;
}
-WRITE_LINE_MEMBER(upd7759_device::md_w)
+void upd7759_device::md_w(int state)
{
- // When called from machine configs/during start up set the mode pin directly.
- if (m_timer == nullptr)
- {
- m_md = state;
- return;
- }
machine().scheduler().synchronize(timer_expired_delegate(FUNC(upd7759_device::internal_md_w), this), state);
}
-
TIMER_CALLBACK_MEMBER(upd7759_device::internal_md_w)
{
+ m_channel->update();
+
uint8_t old_md = m_md;
m_md = (param != 0);
LOG_STATE("upd7759_md_w: %d->%d\n", old_md, m_md);
- m_channel->update();
-
if (m_state == STATE_IDLE && m_reset)
{
if (old_md && !m_md)
@@ -705,13 +671,7 @@ TIMER_CALLBACK_MEMBER(upd7759_device::internal_md_w)
}
-void upd775x_device::port_w(u8 data)
-{
- machine().scheduler().synchronize(timer_expired_delegate(FUNC(upd775x_device::sync_port_write), this), data);
-}
-
-
-READ_LINE_MEMBER( upd775x_device::busy_r )
+int upd775x_device::busy_r()
{
m_channel->update();
diff --git a/src/devices/sound/upd7759.h b/src/devices/sound/upd7759.h
index 1f1d0e38dfd..a1e42a49283 100644
--- a/src/devices/sound/upd7759.h
+++ b/src/devices/sound/upd7759.h
@@ -20,15 +20,16 @@ class upd775x_device : public device_t,
public:
enum : u32 { STANDARD_CLOCK = 640'000 };
- DECLARE_WRITE_LINE_MEMBER( reset_w );
- DECLARE_WRITE_LINE_MEMBER( start_w );
- DECLARE_READ_LINE_MEMBER( busy_r );
+ void reset_w(int state);
+ void start_w(int state);
+ int busy_r();
virtual void port_w(u8 data);
void set_start_delay(uint32_t data) { m_start_delay = data; }
protected:
- virtual TIMER_CALLBACK_MEMBER(internal_start_w) = 0;
+ virtual TIMER_CALLBACK_MEMBER(internal_start_w);
virtual TIMER_CALLBACK_MEMBER(internal_reset_w);
+ virtual TIMER_CALLBACK_MEMBER(internal_port_w);
enum
{
@@ -59,8 +60,8 @@ protected:
// chip modes
enum
{
- MODE_STAND_ALONE,
- MODE_SLAVE
+ MODE_SLAVE,
+ MODE_STAND_ALONE
};
upd775x_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
@@ -70,7 +71,7 @@ protected:
virtual void device_clock_changed() override;
virtual void device_reset() override;
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
// sound stream update overrides
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
@@ -78,8 +79,6 @@ protected:
void update_adpcm(int data);
virtual void advance_state();
- TIMER_CALLBACK_MEMBER(sync_port_write);
-
// internal state
sound_stream *m_channel; // stream channel for playback
@@ -130,7 +129,7 @@ public:
upd7759_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = STANDARD_CLOCK);
- DECLARE_WRITE_LINE_MEMBER( md_w );
+ void md_w(int state);
protected:
upd7759_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
@@ -138,9 +137,6 @@ protected:
virtual void device_start() override;
virtual void device_reset() override;
- virtual TIMER_CALLBACK_MEMBER(internal_start_w) override;
- virtual TIMER_CALLBACK_MEMBER(internal_reset_w) override;
-
TIMER_CALLBACK_MEMBER(drq_update);
void internal_md_w(int state);
@@ -158,8 +154,6 @@ protected:
upd7756_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
virtual void device_reset() override;
-
- virtual TIMER_CALLBACK_MEMBER(internal_start_w) override;
};
DECLARE_DEVICE_TYPE(UPD7759, upd7759_device)
diff --git a/src/devices/sound/upd933.cpp b/src/devices/sound/upd933.cpp
new file mode 100644
index 00000000000..6167ec2a3c1
--- /dev/null
+++ b/src/devices/sound/upd933.cpp
@@ -0,0 +1,636 @@
+// license:BSD-3-Clause
+// copyright-holders:Devin Acker
+
+/***************************************************************************
+ NEC/Casio uPD933 "Phase Distortion" synthesis chip
+***************************************************************************/
+
+#include "emu.h"
+#include "upd933.h"
+
+#include <algorithm>
+#include <climits>
+#include <cmath>
+
+DEFINE_DEVICE_TYPE(UPD933, upd933_device, "upd933", "NEC uPD933")
+
+upd933_device::upd933_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, UPD933, tag, owner, clock)
+ , device_sound_interface(mconfig, *this)
+ , m_irq_cb(*this)
+{
+}
+
+/**************************************************************************/
+void upd933_device::device_start()
+{
+ m_stream = stream_alloc(0, 1, clock() / CLOCKS_PER_SAMPLE);
+
+ m_irq_timer = timer_alloc(FUNC(upd933_device::timer_tick), this);
+
+ for (int i = 0; i < 0x800; i++)
+ m_cosine[i] = 0xfff * (1 - cos(2.0 * M_PI * i / 0x7ff)) / 2;
+
+ for (int i = 0; i < 0x80; i++)
+ {
+ // A4 is note 62, 442 Hz
+ const double freq = 442.0 * pow(2, (i - 62) / 12.0);
+ m_pitch[i] = (1 << PITCH_SHIFT) * (freq * 0x800 / 40000);
+ }
+
+ for (int i = 0; i < 0x200; i++)
+ m_pitch_fine[i] = (1 << PITCH_FINE_SHIFT) * (pow(2, (double)i / (12.0 * 0x200)) - 1);
+
+ // logarithmic volume curve, also scales 12-bit waveform to 13-bit range
+ // (also allows pitch modulation to cover the same spectrum with no extra scaling)
+ for (int i = 1; i < 0x200; i++)
+ m_volume[i] = pow(2 << VOLUME_SHIFT, (double)i / 0x1ff);
+ m_volume[0] = 0;
+
+ m_cs = m_id = 1;
+
+ save_item(NAME(m_irq_pending));
+ save_item(NAME(m_irq_state));
+ save_item(NAME(m_cs));
+ save_item(NAME(m_id));
+ save_item(NAME(m_sound_data));
+ save_item(NAME(m_sound_data_pos));
+ save_item(NAME(m_sound_regs));
+ save_item(NAME(m_sample_count));
+ save_item(NAME(m_last_sample));
+
+ save_item(STRUCT_MEMBER(m_voice, m_wave));
+ save_item(STRUCT_MEMBER(m_voice, m_window));
+ save_item(STRUCT_MEMBER(m_voice, m_ring_mod));
+ save_item(STRUCT_MEMBER(m_voice, m_pitch_mod));
+ save_item(STRUCT_MEMBER(m_voice, m_mute_other));
+ save_item(STRUCT_MEMBER(m_voice, m_pitch));
+ save_item(STRUCT_MEMBER(m_voice, m_position));
+ save_item(STRUCT_MEMBER(m_voice, m_pitch_step));
+ save_item(STRUCT_MEMBER(m_voice, m_dcw_limit));
+ save_item(STRUCT_MEMBER(m_voice, m_pm_level));
+
+ save_item(STRUCT_MEMBER(m_dca, m_direction));
+ save_item(STRUCT_MEMBER(m_dca, m_sustain));
+ save_item(STRUCT_MEMBER(m_dca, m_irq));
+ save_item(STRUCT_MEMBER(m_dca, m_rate));
+ save_item(STRUCT_MEMBER(m_dca, m_target));
+ save_item(STRUCT_MEMBER(m_dca, m_current));
+
+ save_item(STRUCT_MEMBER(m_dcw, m_direction));
+ save_item(STRUCT_MEMBER(m_dcw, m_sustain));
+ save_item(STRUCT_MEMBER(m_dcw, m_irq));
+ save_item(STRUCT_MEMBER(m_dcw, m_rate));
+ save_item(STRUCT_MEMBER(m_dcw, m_target));
+ save_item(STRUCT_MEMBER(m_dcw, m_current));
+
+ save_item(STRUCT_MEMBER(m_dco, m_direction));
+ save_item(STRUCT_MEMBER(m_dco, m_sustain));
+ save_item(STRUCT_MEMBER(m_dco, m_irq));
+ save_item(STRUCT_MEMBER(m_dco, m_rate));
+ save_item(STRUCT_MEMBER(m_dco, m_target));
+ save_item(STRUCT_MEMBER(m_dco, m_current));
+}
+
+/**************************************************************************/
+TIMER_CALLBACK_MEMBER(upd933_device::timer_tick)
+{
+ m_irq_pending = 1;
+ update_irq();
+}
+
+/**************************************************************************/
+void upd933_device::device_reset()
+{
+ m_irq_pending = m_irq_state = 0;
+
+ m_sound_data[0] = m_sound_data[1] = 0;
+ m_sound_data_pos = 0;
+ std::fill(m_sound_regs.begin(), m_sound_regs.end(), 0);
+
+ std::fill(m_voice.begin(), m_voice.end(), voice_t());
+ std::fill(m_dca.begin(), m_dca.end(), env_t());
+ std::fill(m_dco.begin(), m_dco.end(), env_t());
+ std::fill(m_dcw.begin(), m_dcw.end(), env_t());
+
+ m_sample_count = 0;
+ m_last_sample = 0;
+
+ m_irq_timer->adjust(attotime::never);
+ m_irq_cb(0);
+}
+
+/**************************************************************************/
+void upd933_device::device_clock_changed()
+{
+ m_stream->set_sample_rate(clock() / CLOCKS_PER_SAMPLE);
+}
+
+/**************************************************************************/
+int upd933_device::rq_r()
+{
+ if (!machine().side_effects_disabled())
+ m_stream->update();
+
+ return m_irq_state;
+}
+
+/**************************************************************************/
+void upd933_device::cs_w(int state)
+{
+ m_stream->update();
+
+ if (!m_cs && state)
+ update_pending_irq();
+ m_cs = state;
+ update_irq();
+}
+
+/**************************************************************************/
+void upd933_device::id_w(int state)
+{
+ m_stream->update();
+
+ m_id = state;
+ update_irq();
+}
+
+/**************************************************************************/
+u8 upd933_device::irq_data()
+{
+ // TODO: do these have the correct priority?
+ for (int i = 0; i < 8; i++)
+ {
+ if (m_dco[i].m_irq)
+ {
+ if (!machine().side_effects_disabled())
+ m_dco[i].m_irq = false;
+ return 4 | (i << 3);
+ }
+ }
+ for (int i = 0; i < 8; i++)
+ {
+ if (m_dcw[i].m_irq)
+ {
+ if (!machine().side_effects_disabled())
+ m_dcw[i].m_irq = false;
+ return 2 | (i << 2);
+ }
+ }
+ for (int i = 0; i < 8; i++)
+ {
+ if (m_dca[i].m_irq)
+ {
+ if (!machine().side_effects_disabled())
+ m_dca[i].m_irq = false;
+ return 1 | (i << 1);
+ }
+ }
+ return 0;
+}
+
+/**************************************************************************/
+void upd933_device::update_pending_irq()
+{
+ m_irq_pending = 0;
+ bool env_active = false;
+ unsigned new_time = UINT_MAX;
+
+ for (int i = 0; i < 8; i++)
+ {
+ env_active |= (m_dca[i].calc_timeout(new_time)
+ | m_dco[i].calc_timeout(new_time)
+ | m_dcw[i].calc_timeout(new_time));
+ }
+
+ if (env_active)
+ m_irq_timer->adjust(clocks_to_attotime((u64)new_time * CLOCKS_PER_SAMPLE));
+ else
+ m_irq_timer->adjust(attotime::never);
+}
+
+/**************************************************************************/
+void upd933_device::update_irq()
+{
+ u8 const irq_state = m_cs & m_id & m_irq_pending;
+ if (irq_state != m_irq_state)
+ {
+ m_irq_state = irq_state;
+ m_irq_cb(m_irq_state);
+ }
+}
+
+/**************************************************************************/
+u8 upd933_device::read()
+{
+ if (!machine().side_effects_disabled())
+ m_stream->update();
+
+ return m_cs ? 0xff : irq_data();
+}
+
+/**************************************************************************/
+void upd933_device::write(u8 data)
+{
+ if (m_cs) return;
+
+ if (m_sound_data_pos >= 2)
+ {
+ m_stream->update();
+
+ bool ok = true;
+ const u8 reg = m_sound_data[0];
+ const u16 value = m_sound_regs[reg] = (m_sound_data[1] << 8) | data;
+
+ // the low 3 bits of the register number determine which voice is controlled by per-voice registers...
+ const int vnum = reg & 7;
+ voice_t &voice = m_voice[vnum];
+ // ...except for registers 68-6f, which control waveform for voice 'n', but modulation for voice 'n-2'
+ // (even though those two voices don't actually modulate each other...)
+ voice_t &mod_voice = m_voice[(vnum + 6) & 7];
+
+ m_sound_data_pos = 0;
+ switch (reg >> 3)
+ {
+ case 0x0: // 00-07: DCA step (volume envelope)
+ /*
+ msb lsb
+ n------- - direction (0 = up, 1 = down)
+ -nnnnnnn - rate
+ n------- - sustain flag
+ -nnnnnnn - level
+ */
+ {
+ env_t &dca = m_dca[vnum];
+ dca.m_direction = BIT(value, 15);
+ dca.m_rate = env_rate(BIT(value, 8, 7));
+ dca.m_sustain = BIT(value, 7);
+ dca.m_target = BIT(value, 0, 7) << (ENV_DCA_SHIFT + 2);
+ dca.m_irq = false;
+ }
+ break;
+
+ case 0x2: // 10-17: DCO step (pitch envelope)
+ /*
+ msb lsb
+ n------- - direction (0 = up, 1 = down)
+ -nnnnnnn - rate
+ n------- - sustain flag
+ -n------ - level units (1 = 2-semitone intervals, 0 = 6.25-cent intervals)
+ --nnnnnn - level
+ */
+ {
+ env_t &dco = m_dco[vnum];
+ dco.m_direction = BIT(value, 15);
+ dco.m_rate = env_rate(BIT(value, 8, 7));
+ dco.m_sustain = BIT(value, 7);
+ dco.m_target = BIT(value, 0, 6) << (ENV_DCO_SHIFT + 5);
+ if (BIT(value, 6))
+ dco.m_target <<= 5;
+ dco.m_irq = false;
+ }
+ break;
+
+ case 0x4: // 20-27: DCW step (waveform envelope)
+ // same bits as DCA step
+ {
+ env_t &dcw = m_dcw[vnum];
+ dcw.m_direction = BIT(value, 15);
+ dcw.m_rate = env_rate(BIT(value, 8, 7));
+ dcw.m_sustain = BIT(value, 7);
+ dcw.m_target = BIT(value, 0, 7) << (ENV_DCW_SHIFT + 3);
+ dcw.m_irq = false;
+ }
+ break;
+
+ case 0xc: // 60-67: pitch (in semitones, as 7.9 fixed point)
+ voice.m_pitch = value;
+ update_pitch_step(vnum);
+ break;
+
+ case 0xd: // 68-6f: waveform
+ /*
+ msb lsb
+ nnn----- - first waveform
+ ---nnn-- - second waveform
+ ------n- - enable second
+ -------n nn------ - window function
+ --n----- - ring modulation enable
+ ---n---- - pitch modulation enable
+ ----n--- - pitch modulation source (0 = other voice, 1 = noise)
+ -----n-- - output (0 = normal, 1 = mute previous voice)
+ */
+ voice.m_wave[0] = BIT(value, 13, 3);
+ if (BIT(value, 9))
+ voice.m_wave[1] = BIT(value, 10, 3);
+ else
+ voice.m_wave[1] = voice.m_wave[0];
+ voice.m_window = BIT(value, 6, 3);
+ if (!BIT(vnum, 0))
+ {
+ // see earlier comment - these bits actually control a different voice
+ mod_voice.m_ring_mod = BIT(value, 5);
+ mod_voice.m_pitch_mod = BIT(value, 3, 2);
+ mod_voice.m_mute_other = BIT(value, 2);
+ }
+ break;
+
+ case 0x13: // 98-9f: phase counter
+ /*
+ cz101 sets these to zero when starting a note to reset the oscillator.
+ cz1 writes 0x0000, 0x0080, 0x0100, or 0x0180 for up to four voices of a tone instead
+ */
+ voice.m_position = value << (PITCH_SHIFT - 4);
+ break;
+
+ case 0x17: // b8-bb: pitch modulator (probably - cz1 sets to zero when disabling noise)
+ if (vnum < 4)
+ m_voice[vnum << 1].m_pm_level = (s16)value;
+ else
+ ok = false;
+ break;
+
+ default:
+ ok = false;
+ break;
+ }
+
+ if (!ok)
+ logerror("%s: unknown sound reg write: %02x %04x\n", machine().describe_context(), reg, value);
+ }
+ else
+ {
+ m_sound_data[m_sound_data_pos++] = data;
+ }
+}
+
+/**************************************************************************/
+u32 upd933_device::env_rate(u8 data) const
+{
+ return (8 | (data & 7)) << (data >> 3);
+}
+
+/**************************************************************************/
+void upd933_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
+{
+ for (int i = 0; i < outputs[0].samples(); i++)
+ {
+ s32 sample = 0;
+
+ /*
+ Voices need to be processed in a certain order for modulation to work correctly,
+ i.e. to match each odd-numbered voice ("line 1") with the corresponding even-numbered one ("line 2").
+ */
+ static const int voice_map[] = {5, 0, 7, 2, 1, 4, 3, 6};
+ for (int j : voice_map)
+ sample += update(j);
+
+ outputs[0].put_int_clamp(i, sample, 1 << 15);
+ m_sample_count++;
+ }
+}
+
+/**************************************************************************/
+s16 upd933_device::update(int vnum)
+{
+ voice_t &voice = m_voice[vnum];
+ s16 sample = 0;
+
+ const u16 pos = BIT(voice.m_position, PITCH_SHIFT, 11);
+ const u8 wave = BIT(voice.m_position, PITCH_SHIFT + 11);
+
+ const u16 dcw = std::min(u16(m_dcw[vnum].m_current >> ENV_DCW_SHIFT), voice.m_dcw_limit);
+ const u16 pivot = 0x400 - dcw;
+ u16 phase = 0;
+ u16 window = 0;
+
+ //
+ // apply transfer function
+ //
+ switch (voice.m_wave[wave] & 7)
+ {
+ case 0: // sawtooth - rises from [0, pivot) and falls from [pivot, 800)
+ if (pos < pivot)
+ phase = pos * 0x400 / pivot;
+ else
+ phase = 0x400 + (pos - pivot) * 0x400 / (0x800 - pivot);
+ break;
+
+ case 1: // square - rises from [0, pivot), stays high from [pivot, 400), then inverts
+ if ((pos & 0x3ff) < pivot)
+ phase = (pos & 0x3ff) * 0x400 / pivot;
+ else
+ phase = 0x3ff;
+
+ phase |= (pos & 0x400);
+ break;
+
+ case 2: // pulse - rises & falls from [0, pivot*2), then stays low
+ if (pos < pivot * 2)
+ phase = pos * 0x800 / (pivot * 2);
+ else
+ phase = 0x7ff;
+ break;
+
+ case 3: // silent (undocumented)
+ break;
+
+ case 4: // double sine - rises & falls from [0, pivot), then again from [pivot, 800)
+ if (pos < pivot)
+ phase = pos * 0x800 / pivot;
+ else
+ phase = (pos - pivot) * 0x800 / (0x800 - pivot);
+ break;
+
+ case 5: // saw pulse - rises from [0, 400), falls from [400, 400+pivot), then stays low
+ if (pos < 0x400)
+ phase = pos;
+ else if (pos < (pivot + 0x400))
+ phase = 0x400 + (pos & 0x3ff) * 0x400 / pivot;
+ else
+ phase = 0x7ff;
+ break;
+
+ case 6: // resonance
+ // this is a special case that just multiplies the frequency by the DCW level...
+ phase = pos + ((pos * dcw) >> 6);
+ // ...and hardsyncs to the fundamental frequency
+ phase &= 0x7ff;
+ break;
+
+ case 7: // double pulse (undocumented) - same as regular pulse but double frequency
+ if ((pos & 0x3ff) < pivot)
+ phase = (pos & 0x3ff) * 0x400 / pivot;
+ else
+ phase = 0x7ff;
+ break;
+ }
+
+ //
+ // apply window function
+ //
+ switch (voice.m_window & 7)
+ {
+ case 0: // none
+ break;
+
+ case 1: // sawtooth - falls from [0, 800)
+ window = pos;
+ break;
+
+ case 2: // triangle - rises from [0, 400), falls from [400, 800)
+ window = (pos & 0x3ff) * 2;
+ if (pos < 0x400)
+ window ^= 0x7fe;
+ break;
+
+ case 3: // trapezoid - falls from [400, 800)
+ if (pos >= 0x400)
+ window = (pos & 0x3ff) * 2;
+ break;
+
+ case 4: // pulse (undocumented) - falls from [0, 400)
+ if (pos < 0x400)
+ window = pos * 2;
+ else
+ window = 0x7ff;
+ break;
+
+ default: // double saw (undocumented) - rises from [0, 400) and [400, 800)
+ window = (0x3ff ^ (pos & 0x3ff)) * 2;
+ break;
+ }
+
+ sample = m_cosine[phase];
+ if (window)
+ sample = ((s32)sample * (0x800 - window)) / 0x800;
+
+ // center sample around zero, apply volume and ring mod
+ const u16 volume = m_dca[vnum].m_current >> ENV_DCA_SHIFT;
+ sample = ((s32)sample * m_volume[volume]) >> VOLUME_SHIFT;
+ sample -= m_volume[volume] / 2;
+
+ if (voice.m_ring_mod)
+ sample = ((s32)sample * m_last_sample) / 0x1000;
+
+ // 'mute' actually negates the other voice in a modulating pair
+ if (voice.m_mute_other)
+ sample -= m_last_sample;
+
+ //
+ // update envelopes and pitch modulation, recalculate DCO pitch step if needed
+ //
+ const u32 old_dco = m_dco[vnum].m_current;
+ const s16 old_pm = voice.m_pm_level;
+
+ m_dca[vnum].update();
+ m_dcw[vnum].update();
+ m_dco[vnum].update();
+
+ // pitch/noise modulation latches a new pitch multiplier every 8 samples
+ if (!(m_sample_count & 7))
+ {
+ switch (voice.m_pitch_mod & 3)
+ {
+ default:
+ voice.m_pm_level = 0;
+ break;
+
+ case 2:
+ // pitch modulated by other voice (normally unused, up to about +/- 7.5 semitones)
+ voice.m_pm_level = m_last_sample;
+ break;
+
+ case 3:
+ // pitch modulated by noise (0 or 32 semitones above base pitch)
+ voice.m_pm_level = machine().rand() & (32 << NOTE_SHIFT);
+ break;
+ }
+ }
+
+ if ((old_dco ^ m_dco[vnum].m_current) >> ENV_DCO_SHIFT
+ || old_pm != voice.m_pm_level)
+ update_pitch_step(vnum);
+
+ voice.m_position += voice.m_pitch_step;
+
+ m_last_sample = sample;
+ return sample;
+}
+
+/**************************************************************************/
+void upd933_device::env_t::update()
+{
+ if (m_current != m_target)
+ {
+ if (!m_direction) // increasing
+ {
+ if (m_current > m_target
+ || m_target - m_current <= m_rate)
+ m_current = m_target;
+ else
+ m_current += m_rate;
+ }
+ else // decreasing
+ {
+ if (m_current < m_target
+ || m_current - m_target <= m_rate)
+ m_current = m_target;
+ else
+ m_current -= m_rate;
+ }
+ }
+
+ if (!m_sustain && (m_current == m_target))
+ m_irq = m_sustain = true; // set sustain too to make sure this only causes an interrupt once
+}
+
+/**************************************************************************/
+bool upd933_device::env_t::calc_timeout(unsigned &samples)
+{
+ if (m_irq)
+ {
+ samples = 0;
+ }
+ else if (m_sustain || !m_rate)
+ {
+ return false;
+ }
+ else
+ {
+ const unsigned remaining = m_direction ? (m_current - m_target) : (m_target - m_current);
+ unsigned new_time = remaining / m_rate;
+ if (remaining % m_rate)
+ new_time++;
+ if (new_time < samples)
+ samples = new_time;
+ }
+
+ return true;
+}
+
+/**************************************************************************/
+void upd933_device::update_pitch_step(int vnum)
+{
+ voice_t &voice = m_voice[vnum];
+ const s32 pitch = s32(voice.m_pitch + (m_dco[vnum].m_current >> ENV_DCO_SHIFT)) + voice.m_pm_level;
+ u32 step = 0;
+
+ if (pitch > 0 && pitch < (1 << 16))
+ {
+ const u8 note = pitch >> NOTE_SHIFT;
+ const u16 fine = pitch & ((1 << NOTE_SHIFT) - 1);
+ step = m_pitch[note];
+ if (fine)
+ step += (step >> PITCH_FINE_SHIFT) * m_pitch_fine[fine];
+ }
+
+ voice.m_pitch_step = step;
+
+ /*
+ The effective DCW envelope value is limited for higher pitch values.
+ This allows e.g. narrow pulse waves to remain correctly audible
+ and also prevents aliasing noise for extremely high pitch values.
+ */
+ voice.m_dcw_limit = 0x400 - std::min(0x400U, (step >> (PITCH_SHIFT - 2)));
+}
diff --git a/src/devices/sound/upd933.h b/src/devices/sound/upd933.h
new file mode 100644
index 00000000000..b4023fb8311
--- /dev/null
+++ b/src/devices/sound/upd933.h
@@ -0,0 +1,103 @@
+// license:BSD-3-Clause
+// copyright-holders: Devin Acker
+
+/***************************************************************************
+ NEC/Casio uPD933 "Phase Distortion" synthesis chip
+***************************************************************************/
+
+#ifndef MAME_SOUND_UPD933_H
+#define MAME_SOUND_UPD933_H
+
+#pragma once
+
+#include <array>
+
+class upd933_device : public device_t, public device_sound_interface
+{
+public:
+ upd933_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0);
+
+ auto irq_cb() { return m_irq_cb.bind(); }
+
+ int rq_r();
+ void cs_w(int state); // chip select, active low
+ void id_w(int state); // irq disable, active low
+
+ void write(u8 data);
+ u8 read();
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_clock_changed() override;
+
+ virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
+
+private:
+ static constexpr unsigned NOTE_SHIFT = 9;
+ static constexpr unsigned PITCH_SHIFT = 20;
+ static constexpr unsigned PITCH_FINE_SHIFT = 12;
+ static constexpr unsigned VOLUME_SHIFT = 12;
+
+ static constexpr unsigned ENV_DCA_SHIFT = 16;
+ static constexpr unsigned ENV_DCW_SHIFT = 16;
+ static constexpr unsigned ENV_DCO_SHIFT = 11;
+
+ struct env_t
+ {
+ u8 m_direction = 0, m_sustain = 1, m_irq = 0;
+ u32 m_rate = 0, m_target = 0, m_current = 0;
+
+ void update();
+ // calculate the next time this envelope generates an interrupt
+ bool calc_timeout(unsigned &samples);
+ };
+
+ struct voice_t
+ {
+ u8 m_wave[2] = {0};
+ u8 m_window = 0, m_ring_mod = 0, m_pitch_mod = 0, m_mute_other = 0;
+
+ u16 m_pitch = 0;
+ u32 m_position = 0, m_pitch_step = 0;
+ u16 m_dcw_limit = 0;
+ s16 m_pm_level = 0;
+ };
+
+ TIMER_CALLBACK_MEMBER(timer_tick);
+
+ s16 update(int vnum);
+ u8 irq_data();
+ void update_pending_irq();
+ void update_irq();
+
+ u32 env_rate(u8 data) const;
+ void update_pitch_step(int vnum);
+
+ sound_stream *m_stream;
+ static constexpr unsigned CLOCKS_PER_SAMPLE = 112;
+
+ devcb_write_line m_irq_cb;
+ u8 m_irq_pending, m_irq_state;
+ u8 m_cs, m_id;
+ emu_timer *m_irq_timer;
+
+ u16 m_cosine[0x800];
+ u32 m_pitch[0x80];
+ u16 m_pitch_fine[0x200];
+ u16 m_volume[0x200];
+
+ u8 m_sound_data[2];
+ u8 m_sound_data_pos;
+ std::array<u16, 256> m_sound_regs;
+
+ u32 m_sample_count;
+ s16 m_last_sample;
+
+ std::array<voice_t, 8> m_voice;
+ std::array<env_t, 8> m_dca, m_dco, m_dcw;
+};
+
+DECLARE_DEVICE_TYPE(UPD933, upd933_device)
+
+#endif // MAME_SOUND_UPD933_H
diff --git a/src/devices/sound/upd934g.cpp b/src/devices/sound/upd934g.cpp
index fc25628abbd..1178ef0a930 100644
--- a/src/devices/sound/upd934g.cpp
+++ b/src/devices/sound/upd934g.cpp
@@ -7,15 +7,16 @@
Percussion Generator
TODO:
- - Play MUTED and ACCENTED
+ - Correct MUTED and ACCENTED (currently just changes volume)
- T1 input
- - 8 channels?
***************************************************************************/
#include "emu.h"
#include "upd934g.h"
+#define VERBOSE (0)
+#include "logmacro.h"
//**************************************************************************
// DEVICE DEFINITIONS
@@ -35,7 +36,7 @@ DEFINE_DEVICE_TYPE(UPD934G, upd934g_device, "upd934g", "NEC uPD934G")
upd934g_device::upd934g_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
device_t(mconfig, UPD934G, tag, owner, clock),
device_sound_interface(mconfig, *this),
- m_data_cb(*this),
+ device_rom_interface(mconfig, *this),
m_stream(nullptr),
m_sample(0),
m_ready(false)
@@ -51,18 +52,12 @@ void upd934g_device::device_start()
// create sound stream
m_stream = stream_alloc(0, 4, 20000);
- // resolve callbacks
- m_data_cb.resolve_safe(0);
-
// register for save states
save_pointer(NAME(m_addr), 16);
- for (unsigned i = 0; i < 4; i++)
- {
- save_item(NAME(m_channel[i].pos), i);
- save_item(NAME(m_channel[i].playing), i);
- save_item(NAME(m_channel[i].volume), i);
- }
+ save_item(STRUCT_MEMBER(m_channel, pos));
+ save_item(STRUCT_MEMBER(m_channel, playing));
+ save_item(STRUCT_MEMBER(m_channel, effect));
save_item(NAME(m_sample));
save_item(NAME(m_ready));
@@ -95,8 +90,13 @@ void upd934g_device::sound_stream_update(sound_stream &stream, std::vector<read_
for (unsigned i = 0; i < outputs[ch].samples(); i++)
{
- int8_t raw = static_cast<int8_t>(m_data_cb(m_channel[ch].pos));
- outputs[ch].put_int(i, raw * (m_channel[ch].volume + 1), 32768 / 64);
+ int16_t raw = static_cast<int8_t>(read_byte(m_channel[ch].pos)) * 4;
+
+ // normal, muted, accented
+ const double adjust[] = { 0, 0.7, 0.4, 1.0 };
+ raw *= adjust[m_channel[ch].effect];
+
+ outputs[ch].put_int(i, raw, 32768 / 64);
if (++m_channel[ch].pos >= end)
{
@@ -124,28 +124,25 @@ void upd934g_device::write(offs_t offset, uint8_t data)
// format of data written here is:
// 76------ command
// --5432-- sample number
- // ------10 volume?
+ // ------10 output channel
m_sample = (data >> 2) & 0x0f;
+
switch (data >> 6)
{
case 0:
- logerror("CMD STORE ADDRESS sample %x\n", m_sample);
+ LOG("CMD STORE ADDRESS sample %x\n", m_sample);
break;
- case 1:
- logerror("CMD PLAY sample %x (channel %d)\n", m_sample, m_sample >> 1);
- if (m_sample < 8)
+ case 1: // normal
+ case 2: // muted
+ case 3: // accented
{
- m_channel[m_sample >> 1].pos = m_addr[m_sample];
- m_channel[m_sample >> 1].playing = m_sample;
- m_channel[m_sample >> 1].volume = data & 0x03;
+ const u8 ch = (data & 3) ^ 2; // effective order seems to be "2, 3, 0, 1"
+ LOG("CMD PLAY sample %x (channel %d, effect %d)\n", m_sample, ch, data >> 6);
+ m_channel[ch].pos = m_addr[m_sample];
+ m_channel[ch].playing = m_sample;
+ m_channel[ch].effect = data >> 6;
}
break;
- case 2:
- logerror("CMD PLAY MUTED sample %x (channel %d)\n", m_sample, m_sample >> 1);
- break;
- case 3:
- logerror("CMD PLAY ACCENTED sample %x (channel %d)\n", m_sample, m_sample >> 1);
- break;
}
break;
case 1:
@@ -153,7 +150,7 @@ void upd934g_device::write(offs_t offset, uint8_t data)
break;
case 2:
m_addr[m_sample] = (m_addr[m_sample] & 0x00ff) | (data << 8);
- logerror(" sample %x address = %04x\n", m_sample, m_addr[m_sample]);
+ LOG(" sample %x address = %04x\n", m_sample, m_addr[m_sample]);
break;
case 3:
m_ready = true;
diff --git a/src/devices/sound/upd934g.h b/src/devices/sound/upd934g.h
index c61ac5a4910..2799c4d0984 100644
--- a/src/devices/sound/upd934g.h
+++ b/src/devices/sound/upd934g.h
@@ -13,12 +13,13 @@
#pragma once
+#include "dirom.h"
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
-class upd934g_device : public device_t, public device_sound_interface
+class upd934g_device : public device_t, public device_sound_interface, public device_rom_interface<16>
{
public:
static constexpr feature_type imperfect_features() { return feature::SOUND; }
@@ -26,9 +27,6 @@ public:
// construction/destruction
upd934g_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
- // configuration
- auto data_callback() { return m_data_cb.bind(); }
-
void write(offs_t offset, uint8_t data);
protected:
@@ -39,7 +37,6 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
private:
- devcb_read8 m_data_cb;
sound_stream *m_stream;
uint16_t m_addr[16];
@@ -48,7 +45,7 @@ private:
{
uint16_t pos;
int playing;
- int volume;
+ int effect;
}
m_channel[4];
diff --git a/src/devices/sound/vgm_visualizer.cpp b/src/devices/sound/vgm_visualizer.cpp
index bab3c909ddf..e62ffe07785 100644
--- a/src/devices/sound/vgm_visualizer.cpp
+++ b/src/devices/sound/vgm_visualizer.cpp
@@ -11,7 +11,7 @@
***************************************************************************/
#include "emu.h"
-#include "sound/vgm_visualizer.h"
+#include "vgm_visualizer.h"
#include "wdlfft/fft.h"
@@ -88,7 +88,7 @@ void vgmviz_device::device_start()
void vgmviz_device::fill_window()
{
- float window_pos_delta = (3.14159265358979f * 2) / FFT_LENGTH;
+ float window_pos_delta = (M_PI * 2) / FFT_LENGTH;
float power = 0;
for (int i = 0; i < (FFT_LENGTH / 2) + 1; i++)
{
diff --git a/src/devices/sound/vlm5030.cpp b/src/devices/sound/vlm5030.cpp
index 71dd026000c..03381d3460f 100644
--- a/src/devices/sound/vlm5030.cpp
+++ b/src/devices/sound/vlm5030.cpp
@@ -1,12 +1,12 @@
// license:BSD-3-Clause
// copyright-holders:Tatsuyuki Satoh
/*
- vlm5030.c
+ vlm5030.cpp
Sanyo VLM5030 emulator
Written by Tatsuyuki Satoh
- Based on TMS5220 simulator (tms5220.c)
+ Based on TMS5220 simulator (tms5220.cpp)
+-------,_,-------+
GND -- | 1 40 | <- RST
@@ -98,7 +98,7 @@ chirp 4- 5: volume 4- 2 : with filter
chirp 6-11: volume 2- 0 : with filter
chirp 12-..: vokume 0 : silent
- ---------- digial output information ----------
+ ---------- digital output information ----------
when ME pin = high , some status output to A0..15 pins
A0..8 : DAC output value (abs)
@@ -159,41 +159,41 @@ static const int vlm5030_speed_table[8] =
DEFINE_DEVICE_TYPE(VLM5030, vlm5030_device, "vlm5030", "Sanyo VLM5030")
-vlm5030_device::vlm5030_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, VLM5030, tag, owner, clock),
- device_sound_interface(mconfig, *this),
- device_rom_interface(mconfig, *this),
- m_channel(nullptr),
- m_coeff(nullptr),
- m_address(0),
- m_pin_BSY(0),
- m_pin_ST(0),
- m_pin_VCU(0),
- m_pin_RST(0),
- m_latch_data(0),
- m_vcu_addr_h(0),
- m_parameter(0),
- m_phase(PH_RESET),
- m_frame_size(0),
- m_pitch_offset(0),
- m_interp_step(0),
- m_interp_count(0),
- m_sample_count(0),
- m_pitch_count(0),
- m_old_energy(0),
- m_old_pitch(0),
- m_target_energy(0),
- m_target_pitch(0),
- m_new_energy(0),
- m_new_pitch(0),
- m_current_energy(0),
- m_current_pitch(0)
+vlm5030_device::vlm5030_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, VLM5030, tag, owner, clock),
+ device_sound_interface(mconfig, *this),
+ device_rom_interface(mconfig, *this),
+ m_channel(nullptr),
+ m_coeff(nullptr),
+ m_address(0),
+ m_pin_BSY(0),
+ m_pin_ST(0),
+ m_pin_VCU(0),
+ m_pin_RST(0),
+ m_latch_data(0),
+ m_vcu_addr_h(0),
+ m_parameter(0),
+ m_phase(PH_RESET),
+ m_frame_size(0),
+ m_pitch_offset(0),
+ m_interp_step(0),
+ m_interp_count(0),
+ m_sample_count(0),
+ m_pitch_count(0),
+ m_old_energy(0),
+ m_old_pitch(0),
+ m_target_energy(0),
+ m_target_pitch(0),
+ m_new_energy(0),
+ m_new_pitch(0),
+ m_current_energy(0),
+ m_current_pitch(0)
{
- memset(m_old_k, 0, sizeof(m_old_k));
- memset(m_new_k, 0, sizeof(m_new_k));
- memset(m_current_k, 0, sizeof(m_current_k));
- memset(m_target_k, 0, sizeof(m_target_k));
- memset(m_x, 0, sizeof(m_x));
+ memset(m_old_k, 0, sizeof(m_old_k));
+ memset(m_new_k, 0, sizeof(m_new_k));
+ memset(m_current_k, 0, sizeof(m_current_k));
+ memset(m_target_k, 0, sizeof(m_target_k));
+ memset(m_x, 0, sizeof(m_x));
}
//-------------------------------------------------
@@ -208,7 +208,7 @@ void vlm5030_device::device_start()
m_coeff = &vlm5030_coeff;
/* reset input pins */
- m_pin_RST = m_pin_ST = m_pin_VCU= 0;
+ m_pin_RST = m_pin_ST = m_pin_VCU = 0;
m_latch_data = 0;
device_reset();
@@ -259,7 +259,7 @@ void vlm5030_device::device_reset()
m_interp_count = m_sample_count = m_pitch_count = 0;
memset(m_x, 0, sizeof(m_x));
/* reset parameters */
- setup_parameter( 0x00);
+ setup_parameter(0x00);
}
void vlm5030_device::device_post_load()
@@ -267,7 +267,7 @@ void vlm5030_device::device_post_load()
restore_state();
}
-void vlm5030_device::rom_bank_updated()
+void vlm5030_device::rom_bank_pre_change()
{
m_channel->update();
}
@@ -287,38 +287,41 @@ int vlm5030_device::get_bits(int sbit,int bits)
/* get next frame */
int vlm5030_device::parse_frame()
{
- unsigned char cmd;
- int i;
-
/* remember previous frame */
m_old_energy = m_new_energy;
m_old_pitch = m_new_pitch;
- for(i=0;i<=9;i++)
+ for (int i = 0; i <= 9; i++)
m_old_k[i] = m_new_k[i];
/* command byte check */
- cmd = read_byte(m_address);
- if( cmd & 0x01 )
- { /* extend frame */
+ uint8_t cmd = read_byte(m_address);
+ if (cmd & 0x01)
+ {
+ /* extend frame */
m_new_energy = m_new_pitch = 0;
- for(i=0;i<=9;i++)
+ for (int i = 0; i <= 9; i++)
m_new_k[i] = 0;
m_address++;
- if( cmd & 0x02 )
- { /* end of speech */
-
- /* logerror("VLM5030 %04X end \n",m_address ); */
+ if (cmd & 0x02)
+ {
+ /* end of speech */
+ /* logerror("VLM5030 %04X end \n", m_address); */
return 0;
}
else
- { /* silent frame */
- int nums = ( (cmd>>2)+1 )*2;
- /* logerror("VLM5030 %04X silent %d frame\n",m_address,nums ); */
+ {
+ /* silent frame */
+ int nums = ((cmd >> 2) + 1) * 2;
+ /* logerror("VLM5030 %04X silent %d frame\n", m_address, nums); */
return nums * FR_SIZE;
}
}
+
/* pitch */
- m_new_pitch = ( m_coeff->pitchtable[get_bits(1,m_coeff->pitch_bits)] + m_pitch_offset )&0xff;
+ m_new_pitch = m_coeff->pitchtable[get_bits(1,m_coeff->pitch_bits)];
+ if (m_new_pitch > 0)
+ m_new_pitch += m_pitch_offset;
+
/* energy */
m_new_energy = m_coeff->energytable[get_bits(6,m_coeff->energy_bits)];
@@ -334,8 +337,8 @@ int vlm5030_device::parse_frame()
m_new_k[1] = m_coeff->ktable[1][get_bits(37,m_coeff->kbits[1])];
m_new_k[0] = m_coeff->ktable[0][get_bits(42,m_coeff->kbits[0])];
- m_address+=6;
- logerror("VLM5030 %04X voice \n",m_address );
+ m_address += 6;
+ logerror("VLM5030 %04X voice \n", m_address);
//fprintf(stderr,"*** target Energy, Pitch, and Ks = %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d\n",m_new_energy, m_new_pitch, m_new_k[0], m_new_k[1], m_new_k[2], m_new_k[3], m_new_k[4], m_new_k[5], m_new_k[6], m_new_k[7], m_new_k[8], m_new_k[9]);
return FR_SIZE;
}
@@ -352,21 +355,21 @@ void vlm5030_device::setup_parameter(uint8_t param)
/* latch parameter value */
m_parameter = param;
- /* bit 0,1 : 4800bps / 9600bps , interporator step */
- if(param&2) /* bit 1 = 1 , 9600bps */
- m_interp_step = 4; /* 9600bps : no interporator */
- else if(param&1) /* bit1 = 0 & bit0 = 1 , 4800bps */
- m_interp_step = 2; /* 4800bps : 2 interporator */
+ /* bit 0,1 : 4800bps / 9600bps , interpolator step */
+ if (param & 2) /* bit 1 = 1 , 9600bps */
+ m_interp_step = 4; /* 9600bps : no interpolator */
+ else if (param & 1) /* bit1 = 0 & bit0 = 1 , 4800bps */
+ m_interp_step = 2; /* 4800bps : 2 interpolator */
else /* bit1 = bit0 = 0 : 2400bps */
- m_interp_step = 1; /* 2400bps : 4 interporator */
+ m_interp_step = 1; /* 2400bps : 4 interpolator */
/* bit 3,4,5 : speed (frame size) */
- m_frame_size = vlm5030_speed_table[(param>>3) &7];
+ m_frame_size = vlm5030_speed_table[(param>>3) & 7];
/* bit 6,7 : low / high pitch */
- if(param&0x80) /* bit7=1 , high pitch */
+ if (param & 0x80) /* bit7=1 , high pitch */
m_pitch_offset = -8;
- else if(param&0x40) /* bit6=1 , low pitch */
+ else if (param & 0x40) /* bit6=1 , low pitch */
m_pitch_offset = 8;
else
m_pitch_offset = 0;
@@ -375,50 +378,50 @@ void vlm5030_device::setup_parameter(uint8_t param)
void vlm5030_device::restore_state()
{
- int i;
-
int interp_effect = FR_SIZE - (m_interp_count%FR_SIZE);
/* restore parameter data */
- setup_parameter( m_parameter);
+ setup_parameter(m_parameter);
/* restore current energy,pitch & filter */
m_current_energy = m_old_energy + (m_target_energy - m_old_energy) * interp_effect / FR_SIZE;
if (m_old_pitch > 1)
m_current_pitch = m_old_pitch + (m_target_pitch - m_old_pitch) * interp_effect / FR_SIZE;
- for (i = 0; i <= 9 ; i++)
+ for (int i = 0; i <= 9 ; i++)
m_current_k[i] = m_old_k[i] + (m_target_k[i] - m_old_k[i]) * interp_effect / FR_SIZE;
}
/* get BSY pin level */
-READ_LINE_MEMBER( vlm5030_device::bsy )
+int vlm5030_device::bsy()
{
update();
return m_pin_BSY;
}
-/* latch contoll data */
+/* latch contol data */
void vlm5030_device::data_w(uint8_t data)
{
m_latch_data = data;
}
/* set RST pin level : reset / set table address A8-A15 */
-WRITE_LINE_MEMBER( vlm5030_device::rst )
+void vlm5030_device::rst(int state)
{
- if( m_pin_RST )
+ if (m_pin_RST != state)
{
- if( !state )
- { /* H -> L : latch parameters */
+ /* pin level is changed */
+ update();
+
+ if (!state)
+ {
+ /* H -> L : latch parameters */
m_pin_RST = 0;
- setup_parameter( m_latch_data);
+ setup_parameter(m_latch_data);
}
- }
- else
- {
- if( state )
- { /* L -> H : reset chip */
+ else
+ {
+ /* L -> H : reset chip */
m_pin_RST = 1;
- if( m_pin_BSY )
+ if (m_pin_BSY)
{
device_reset();
}
@@ -427,49 +430,49 @@ WRITE_LINE_MEMBER( vlm5030_device::rst )
}
/* set VCU pin level : ?? unknown */
-WRITE_LINE_MEMBER( vlm5030_device::vcu )
+void vlm5030_device::vcu(int state)
{
/* direct mode / indirect mode */
m_pin_VCU = state;
}
-/* set ST pin level : set table address A0-A7 / start speech */
-WRITE_LINE_MEMBER( vlm5030_device::st )
+/* set ST pin level : set table address A0-A7 / start speech */
+void vlm5030_device::st(int state)
{
- int table;
-
- if( m_pin_ST != state )
+ if (m_pin_ST != state)
{
- /* pin level is change */
- if( !state )
- { /* H -> L */
+ /* pin level is changed */
+ update();
+
+ if (!state)
+ {
+ /* H -> L */
m_pin_ST = 0;
- if( m_pin_VCU )
- { /* direct access mode & address High */
+ if (m_pin_VCU)
+ {
+ /* direct access mode & address High */
m_vcu_addr_h = ((int)m_latch_data<<8) + 0x01;
}
else
{
/* start speech */
/* check access mode */
- if( m_vcu_addr_h )
- { /* direct access mode */
+ if (m_vcu_addr_h)
+ {
+ /* direct access mode */
m_address = (m_vcu_addr_h&0xff00) + m_latch_data;
m_vcu_addr_h = 0;
}
else
- { /* indirect accedd mode */
- table = (m_latch_data&0xfe) + (((int)m_latch_data&1)<<8);
+ {
+ /* indirect access mode */
+ int table = (m_latch_data&0xfe) + (((int)m_latch_data&1)<<8);
m_address = (read_byte(table)<<8) | read_byte(table+1);
-#if 0
-/* show unsupported parameter message */
-if( m_interp_step != 1)
- popmessage("No %d %dBPS parameter",table/2,m_interp_step*2400);
-#endif
+ /* show unsupported parameter message */
+ /* if (m_interp_step != 1) popmessage("No %d %dBPS parameter",table/2,m_interp_step*2400); */
}
- update();
- /* logerror("VLM5030 %02X start adr=%04X\n",table/2,m_address ); */
+ /* logerror("VLM5030 %02X start adr=%04X\n", table/2, m_address); */
/* reset process status */
m_sample_count = m_frame_size;
m_interp_count = FR_SIZE;
@@ -479,7 +482,8 @@ if( m_interp_step != 1)
}
}
else
- { /* L -> H */
+ {
+ /* L -> H */
m_pin_ST = 1;
/* setup speech , BSY on after 30ms? */
m_phase = PH_SETUP;
@@ -503,7 +507,7 @@ void vlm5030_device::sound_stream_update(sound_stream &stream, std::vector<read_
/* running */
int sampindex = 0;
- if( m_phase == PH_RUN || m_phase == PH_STOP )
+ if (m_phase == PH_RUN || m_phase == PH_STOP)
{
/* playing speech */
for ( ; sampindex < buffer.samples(); sampindex++)
@@ -511,38 +515,42 @@ void vlm5030_device::sound_stream_update(sound_stream &stream, std::vector<read_
int current_val;
/* check new interpolator or new frame */
- if( m_sample_count == 0 )
+ if (m_sample_count == 0)
{
- if( m_phase == PH_STOP )
+ if (m_phase == PH_STOP)
{
m_phase = PH_END;
m_sample_count = 1;
goto phase_stop; /* continue to end phase */
}
m_sample_count = m_frame_size;
+
/* interpolator changes */
- if ( m_interp_count == 0 )
+ if (m_interp_count == 0)
{
/* change to new frame */
m_interp_count = parse_frame(); /* with change phase */
- if ( m_interp_count == 0 )
- { /* end mark found */
+ if (m_interp_count == 0)
+ {
+ /* end mark found */
m_interp_count = FR_SIZE;
m_sample_count = m_frame_size; /* end -> stop time */
m_phase = PH_STOP;
}
+
/* Set old target as new start of frame */
m_current_energy = m_old_energy;
m_current_pitch = m_old_pitch;
- for(i=0;i<=9;i++)
+ for (i = 0; i <= 9; i++)
m_current_k[i] = m_old_k[i];
+
/* is this a zero energy frame? */
if (m_current_energy == 0)
{
/*osd_printf_debug("processing frame: zero energy\n");*/
m_target_energy = 0;
m_target_pitch = m_current_pitch;
- for(i=0;i<=9;i++)
+ for (i = 0; i <= 9; i++)
m_target_k[i] = m_current_k[i];
}
else
@@ -552,10 +560,11 @@ void vlm5030_device::sound_stream_update(sound_stream &stream, std::vector<read_
/*osd_printf_debug("proc: %d %d\n",last_fbuf_head,fbuf_head);*/
m_target_energy = m_new_energy;
m_target_pitch = m_new_pitch;
- for(i=0;i<=9;i++)
+ for (i = 0; i <= 9; i++)
m_target_k[i] = m_new_k[i];
}
}
+
/* next interpolator */
/* Update values based on step values 25% , 50% , 75% , 100% */
m_interp_count -= m_interp_step;
@@ -567,20 +576,22 @@ void vlm5030_device::sound_stream_update(sound_stream &stream, std::vector<read_
for (i = 0; i <= 9 ; i++)
m_current_k[i] = m_old_k[i] + (m_target_k[i] - m_old_k[i]) * interp_effect / FR_SIZE;
}
- /* calcrate digital filter */
+
+ /* calculate digital filter */
if (m_old_energy == 0)
{
/* generate silent samples here */
current_val = 0x00;
}
else if (m_old_pitch <= 1)
- { /* generate unvoiced samples here */
+ {
+ /* generate unvoiced samples here */
current_val = (machine().rand()&1) ? m_current_energy : -m_current_energy;
}
else
{
/* generate voiced samples here */
- current_val = ( m_pitch_count == 0) ? m_current_energy : 0;
+ current_val = (m_pitch_count == 0) ? m_current_energy : 0;
}
/* Lattice filter here */
@@ -596,24 +607,26 @@ void vlm5030_device::sound_stream_update(sound_stream &stream, std::vector<read_
/* sample count */
m_sample_count--;
+
/* pitch */
m_pitch_count++;
- if (m_pitch_count >= m_current_pitch )
+ if (m_pitch_count >= m_current_pitch)
m_pitch_count = 0;
/* size */
}
-/* return;*/
+ /* return; */
}
+
/* stop phase */
phase_stop:
- switch( m_phase )
+ switch (m_phase)
{
case PH_SETUP:
- if( m_sample_count <= buffer.samples())
+ if (m_sample_count <= buffer.samples())
{
m_sample_count = 0;
- /* logerror("VLM5030 BSY=H\n" ); */
- /* pin_BSY = 1; */
+ /* logerror("VLM5030 BSY=H\n"); */
+ /* m_pin_BSY = 1; */
m_phase = PH_WAIT;
}
else
@@ -622,10 +635,10 @@ phase_stop:
}
break;
case PH_END:
- if( m_sample_count <= buffer.samples())
+ if (m_sample_count <= buffer.samples())
{
m_sample_count = 0;
- /* logerror("VLM5030 BSY=L\n" ); */
+ /* logerror("VLM5030 BSY=L\n"); */
m_pin_BSY = 0;
m_phase = PH_IDLE;
}
@@ -634,6 +647,7 @@ phase_stop:
m_sample_count -= buffer.samples();
}
}
+
/* silent buffering */
buffer.fill(0, sampindex);
}
diff --git a/src/devices/sound/vlm5030.h b/src/devices/sound/vlm5030.h
index 4675d99fce4..8f8eeb8bb1b 100644
--- a/src/devices/sound/vlm5030.h
+++ b/src/devices/sound/vlm5030.h
@@ -13,19 +13,19 @@ public:
vlm5030_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
/* get BSY pin level */
- DECLARE_READ_LINE_MEMBER( bsy );
+ int bsy();
/* latch contoll data */
void data_w(uint8_t data);
/* set RST pin level : reset / set table address A8-A15 */
- DECLARE_WRITE_LINE_MEMBER( rst );
+ void rst(int state);
/* set VCU pin level : ?? unknown */
- DECLARE_WRITE_LINE_MEMBER( vcu );
+ void vcu(int state);
/* set ST pin level : set table address A0-A7 / start speech */
- DECLARE_WRITE_LINE_MEMBER( st );
+ void st(int state);
protected:
// device-level overrides
@@ -37,7 +37,7 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
// device_rom_interface overrides
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
private:
// configuration state
@@ -50,7 +50,6 @@ private:
const struct tms5100_coeffs *m_coeff;
/* need to save state */
-
uint16_t m_address;
uint8_t m_pin_BSY;
uint8_t m_pin_ST;
diff --git a/src/devices/sound/votrax.cpp b/src/devices/sound/votrax.cpp
index 1ec2343f72f..f1da1134415 100644
--- a/src/devices/sound/votrax.cpp
+++ b/src/devices/sound/votrax.cpp
@@ -25,12 +25,27 @@ tp1 = phi clock (tied to f2q rom access)
#include "emu.h"
#include "votrax.h"
+#define LOG_PHONE (1U << 1)
+#define LOG_COMMIT (1U << 2)
+#define LOG_INT (1U << 3)
+#define LOG_TICK (1U << 4)
+#define LOG_FILTER (1U << 5)
-DEFINE_DEVICE_TYPE(VOTRAX_SC01, votrax_sc01_device, "votrax", "Votrax SC-01")
+//#define VERBOSE (LOG_GENERAL | LOG_PHONE)
+#include "logmacro.h"
+
+
+DEFINE_DEVICE_TYPE(VOTRAX_SC01, votrax_sc01_device, "votrsc01", "Votrax SC-01")
+DEFINE_DEVICE_TYPE(VOTRAX_SC01A, votrax_sc01a_device, "votrsc01a", "Votrax SC-01-A")
// ROM definition for the Votrax phone ROM
ROM_START( votrax_sc01 )
ROM_REGION64_LE( 0x200, "internal", 0 )
+ ROM_LOAD( "sc01.bin", 0x000, 0x200, CRC(528d1c57) SHA1(268b5884dce04e49e2376df3e2dc82e852b708c1) )
+ROM_END
+
+ROM_START( votrax_sc01a )
+ ROM_REGION64_LE( 0x200, "internal", 0 )
ROM_LOAD( "sc01a.bin", 0x000, 0x200, CRC(fc416227) SHA1(1d6da90b1807a01b5e186ef08476119a862b5e6d) )
ROM_END
@@ -74,9 +89,14 @@ const double votrax_sc01_device::s_glottal_wave[9] =
1/7.0
};
-
votrax_sc01_device::votrax_sc01_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, VOTRAX_SC01, tag, owner, clock),
+ : votrax_sc01_device(mconfig, VOTRAX_SC01, tag, owner, clock)
+{
+}
+
+// overridable type for subclass
+votrax_sc01_device::votrax_sc01_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, type, tag, owner, clock),
device_sound_interface(mconfig, *this),
m_stream(nullptr),
m_rom(*this, "internal"),
@@ -84,6 +104,11 @@ votrax_sc01_device::votrax_sc01_device(const machine_config &mconfig, const char
{
}
+votrax_sc01a_device::votrax_sc01a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : votrax_sc01_device(mconfig, VOTRAX_SC01A, tag, owner, clock)
+{
+}
+
void votrax_sc01_device::write(uint8_t data)
{
// flush out anything currently processing
@@ -95,7 +120,7 @@ void votrax_sc01_device::write(uint8_t data)
m_phone = data & 0x3f;
if(m_phone != prev || m_phone != 0x3f)
- logerror("phone %02x.%d %s\n", m_phone, m_inflection, s_phone_table[m_phone]);
+ LOGMASKED(LOG_PHONE, "phone %02x.%d %s\n", m_phone, m_inflection, s_phone_table[m_phone]);
m_ar_state = CLEAR_LINE;
m_ar_cb(m_ar_state);
@@ -158,6 +183,11 @@ const tiny_rom_entry *votrax_sc01_device::device_rom_region() const
return ROM_NAME( votrax_sc01 );
}
+const tiny_rom_entry *votrax_sc01a_device::device_rom_region() const
+{
+ return ROM_NAME( votrax_sc01a );
+}
+
//-------------------------------------------------
// device_start - handle device startup
@@ -173,7 +203,6 @@ void votrax_sc01_device::device_start()
m_timer = timer_alloc(FUNC(votrax_sc01_device::phone_tick), this);
// reset outputs
- m_ar_cb.resolve_safe();
m_ar_state = ASSERT_LINE;
// save inputs
@@ -388,8 +417,7 @@ void votrax_sc01_device::phone_commit()
// Hard-wired on the die, not an actual part of the rom.
m_rom_pause = (m_phone == 0x03) || (m_phone == 0x3e);
- if(0)
- logerror("commit fa=%x va=%x fc=%x f1=%x f2=%x f2q=%x f3=%x dur=%02x cld=%x vd=%d cl=%d pause=%d\n", m_rom_fa, m_rom_va, m_rom_fc, m_rom_f1, m_rom_f2, m_rom_f2q, m_rom_f3, m_rom_duration, m_rom_cld, m_rom_vd, m_rom_closure, m_rom_pause);
+ LOGMASKED(LOG_COMMIT, "commit fa=%x va=%x fc=%x f1=%x f2=%x f2q=%x f3=%x dur=%02x cld=%x vd=%d cl=%d pause=%d\n", m_rom_fa, m_rom_va, m_rom_fc, m_rom_f1, m_rom_f2, m_rom_f2q, m_rom_f3, m_rom_duration, m_rom_cld, m_rom_vd, m_rom_closure, m_rom_pause);
// That does not happen in the sc01(a) rom, but let's
// cover our behind.
@@ -439,29 +467,30 @@ void votrax_sc01_device::chip_update()
// The formants are frozen on a pause phone unless both voice and
// noise volumes are zero.
if(tick_208 && (!m_rom_pause || !(m_filt_fa || m_filt_va))) {
- // interpolate(m_cur_va, m_rom_va);
+ // interpolate(m_cur_va, m_rom_va);
interpolate(m_cur_fc, m_rom_fc);
interpolate(m_cur_f1, m_rom_f1);
interpolate(m_cur_f2, m_rom_f2);
interpolate(m_cur_f2q, m_rom_f2q);
interpolate(m_cur_f3, m_rom_f3);
- // logerror("int fa=%x va=%x fc=%x f1=%x f2=%02x f2q=%02x f3=%x\n", m_cur_fa >> 4, m_cur_va >> 4, m_cur_fc >> 4, m_cur_f1 >> 4, m_cur_f2 >> 3, m_cur_f2q >> 4, m_cur_f3 >> 4);
+ LOGMASKED(LOG_INT, "int fa=%x va=%x fc=%x f1=%x f2=%02x f2q=%02x f3=%x\n", m_cur_fa >> 4, m_cur_va >> 4, m_cur_fc >> 4, m_cur_f1 >> 4, m_cur_f2 >> 3, m_cur_f2q >> 4, m_cur_f3 >> 4);
}
// Non-formant update. Same bug there, va should be updated, not fc.
if(tick_625) {
if(m_ticks >= m_rom_vd)
interpolate(m_cur_fa, m_rom_fa);
- if(m_ticks >= m_rom_cld)
- // interpolate(m_cur_fc, m_rom_fc);
+ if(m_ticks >= m_rom_cld) {
+ // interpolate(m_cur_fc, m_rom_fc);
interpolate(m_cur_va, m_rom_va);
- // logerror("int fa=%x va=%x fc=%x f1=%x f2=%02x f2q=%02x f3=%x\n", m_cur_fa >> 4, m_cur_va >> 4, m_cur_fc >> 4, m_cur_f1 >> 4, m_cur_f2 >> 3, m_cur_f2q >> 4, m_cur_f3 >> 4);
+ LOGMASKED(LOG_INT, "int fa=%x va=%x fc=%x f1=%x f2=%02x f2q=%02x f3=%x\n", m_cur_fa >> 4, m_cur_va >> 4, m_cur_fc >> 4, m_cur_f1 >> 4, m_cur_f2 >> 3, m_cur_f2q >> 4, m_cur_f3 >> 4);
+ }
}
// Closure counter, reset every other tick in theory when not
// active (on the extra rom cycle).
//
- // The closure level is immediatly used in the analog path,
+ // The closure level is immediately used in the analog path,
// there's no pitch synchronization.
if(!m_cur_closure && (m_filt_fa || m_filt_va))
@@ -490,7 +519,7 @@ void votrax_sc01_device::chip_update()
m_noise = ((m_noise << 1) & 0x7ffe) | inp;
m_cur_noise = !(((m_noise >> 14) ^ (m_noise >> 13)) & 1);
- // logerror("%s tick %02x.%03x 625=%d 208=%d pitch=%02x.%x ns=%04x ni=%d noise=%d cl=%x.%x clf=%d/%d\n", machine().time().to_string(), m_ticks, m_phonetick, tick_625, tick_208, m_pitch >> 3, m_pitch & 7, m_noise, inp, m_cur_noise, m_closure >> 2, m_closure & 3, m_rom_closure, m_cur_closure);
+ LOGMASKED(LOG_TICK, "%s tick %02x.%03x 625=%d 208=%d pitch=%02x.%x ns=%04x ni=%d noise=%d cl=%x.%x clf=%d/%d\n", machine().time().to_string(), m_ticks, m_phonetick, tick_625, tick_208, m_pitch >> 3, m_pitch & 7, m_noise, inp, m_cur_noise, m_closure >> 2, m_closure & 3, m_rom_closure, m_cur_closure);
}
void votrax_sc01_device::filters_commit(bool force)
@@ -563,10 +592,8 @@ void votrax_sc01_device::filters_commit(bool force)
14083);
}
- if(0)
- if(m_filt_fa || m_filt_va || m_filt_fc || m_filt_f1 || m_filt_f2 || m_filt_f2q || m_filt_f3)
- logerror("filter fa=%x va=%x fc=%x f1=%x f2=%02x f2q=%x f3=%x\n",
- m_filt_fa, m_filt_va, m_filt_fc, m_filt_f1, m_filt_f2, m_filt_f2q, m_filt_f3);
+ if(m_filt_fa | m_filt_va | m_filt_fc | m_filt_f1 | m_filt_f2 | m_filt_f2q | m_filt_f3)
+ LOGMASKED(LOG_FILTER, "filter fa=%x va=%x fc=%x f1=%x f2=%02x f2q=%x f3=%x\n", m_filt_fa, m_filt_va, m_filt_fc, m_filt_f1, m_filt_f2, m_filt_f2q, m_filt_f3);
}
stream_buffer::sample_t votrax_sc01_device::analog_calc()
@@ -663,16 +690,16 @@ stream_buffer::sample_t votrax_sc01_device::analog_calc()
defined as the ratio Vo/Vi. To do that, you use some properties:
- The intensity through an element is equal to the voltage
- difference through the element divided by the impedence
+ difference through the element divided by the impedance
- - The impedence of a resistance is equal to its resistance
+ - The impedance of a resistance is equal to its resistance
- - The impedence of a capacitor is 1/(s*C) where C is its capacitance
+ - The impedance of a capacitor is 1/(s*C) where C is its capacitance
- - The impedence of elements in series is the sum of the impedences
+ - The impedance of elements in series is the sum of their impedances
- - The impedence of elements in parallel is the inverse of the sum of
- the inverses
+ - The impedance of elements in parallel is the inverse of the sum of
+ their inverses
- The sum of all intensities flowing into a node is 0 (there's no
charge accumulation in a wire)
@@ -717,7 +744,7 @@ stream_buffer::sample_t votrax_sc01_device::analog_calc()
| H(s) = -------------------------
| 1 + k[1]*s + k[2]*s^2
- We can always reintroduce the global multipler later, and it's 1 in
+ We can always reintroduce the global multiplier later, and it's 1 in
most of our cases anyway.
The we pose:
diff --git a/src/devices/sound/votrax.h b/src/devices/sound/votrax.h
index 2dcc7d087da..900d1f218b9 100644
--- a/src/devices/sound/votrax.h
+++ b/src/devices/sound/votrax.h
@@ -17,8 +17,6 @@ class votrax_sc01_device : public device_t,
public device_sound_interface
{
public:
- static constexpr feature_type imperfect_features() { return feature::SOUND; }
-
// construction/destruction
votrax_sc01_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
@@ -26,9 +24,11 @@ public:
void write(uint8_t data);
void inflection_w(uint8_t data);
- DECLARE_READ_LINE_MEMBER(request) { m_stream->update(); return m_ar_state; }
+ int request() { m_stream->update(); return m_ar_state; }
protected:
+ // overridable type for subclass
+ votrax_sc01_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
// device-level overrides
virtual const tiny_rom_entry *device_rom_region() const override;
virtual void device_start() override;
@@ -181,6 +181,14 @@ private:
stream_buffer::sample_t analog_calc(); // Compute one more sample
};
+class votrax_sc01a_device : public votrax_sc01_device
+{
+public:
+ votrax_sc01a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+protected:
+ // device-level overrides
+ virtual const tiny_rom_entry *device_rom_region() const override;
+};
//**************************************************************************
// GLOBAL VARIABLES
@@ -188,5 +196,6 @@ private:
// device type definition
DECLARE_DEVICE_TYPE(VOTRAX_SC01, votrax_sc01_device)
+DECLARE_DEVICE_TYPE(VOTRAX_SC01A, votrax_sc01a_device)
#endif // MAME_SOUND_VOTRAX_H
diff --git a/src/devices/sound/vrender0.cpp b/src/devices/sound/vrender0.cpp
index fd86b56adea..a364ed8ef67 100644
--- a/src/devices/sound/vrender0.cpp
+++ b/src/devices/sound/vrender0.cpp
@@ -14,11 +14,6 @@
- Sample Rate is unverified
*************************************************************************************/
-static inline s32 sign_ext(s32 val, s32 bit)
-{
- bit = 32 - bit;
- return ((s32)(val << bit)) >> bit;
-}
//Correct table thanks to Evoga
//they left a ulaw<->linear conversion tool inside the roms
@@ -139,8 +134,6 @@ vr0sound_device::vr0sound_device(const machine_config &mconfig, const char *tag,
void vr0sound_device::device_start()
{
- m_irq_cb.resolve_safe();
-
// Find our direct access
space(AS_TEXTURE).cache(m_texcache);
space(AS_FRAME).cache(m_fbcache);
@@ -462,7 +455,7 @@ void vr0sound_device::channel_t::write(offs_t offset, u16 data, u16 mem_mask)
case 0x06/2:
LD = data & 0x1000;
EnvStage = (data & 0x0f00) >> 8;
- EnvVol = sign_ext((EnvVol & 0x00ffff) | ((data << 16) & 0xff0000), 24);
+ EnvVol = util::sext((EnvVol & 0x00ffff) | ((data << 16) & 0xff0000), 24);
break;
case 0x08/2:
dSAddr = data & 0xffff;
@@ -494,8 +487,8 @@ void vr0sound_device::channel_t::write(offs_t offset, u16 data, u16 mem_mask)
case 0x1e/2:
EnvTarget[((offset - (0x1c/2)) * 2) + 0] = (data & 0x007f);
EnvTarget[((offset - (0x1c/2)) * 2) + 1] = ((data & 0x7f00) >> 8);
- EnvRate[((offset - (0x1c/2)) * 2) + 0] = sign_ext((EnvRate[((offset - (0x1c/2)) * 2) + 0] & 0xffff) | ((data & 0x0080) << 9), 17);
- EnvRate[((offset - (0x1c/2)) * 2) + 1] = sign_ext((EnvRate[((offset - (0x1c/2)) * 2) + 1] & 0xffff) | ((data & 0x8000) << 1), 17);
+ EnvRate[((offset - (0x1c/2)) * 2) + 0] = util::sext((EnvRate[((offset - (0x1c/2)) * 2) + 0] & 0xffff) | ((data & 0x0080) << 9), 17);
+ EnvRate[((offset - (0x1c/2)) * 2) + 1] = util::sext((EnvRate[((offset - (0x1c/2)) * 2) + 1] & 0xffff) | ((data & 0x8000) << 1), 17);
break;
}
}
diff --git a/src/devices/sound/x1_010.cpp b/src/devices/sound/x1_010.cpp
index 4ad9993c120..2947ed66654 100644
--- a/src/devices/sound/x1_010.cpp
+++ b/src/devices/sound/x1_010.cpp
@@ -56,13 +56,11 @@ Registers:
#include "x1_010.h"
-#define VERBOSE_SOUND 0
-#define VERBOSE_REGISTER_WRITE 0
-#define VERBOSE_REGISTER_READ 0
-
-#define LOG_SOUND(...) do { if (VERBOSE_SOUND) logerror(__VA_ARGS__); } while (0)
-#define LOG_REGISTER_WRITE(...) do { if (VERBOSE_REGISTER_WRITE) logerror(__VA_ARGS__); } while (0)
-#define LOG_REGISTER_READ(...) do { if (VERBOSE_REGISTER_READ) logerror(__VA_ARGS__); } while (0)
+#define LOG_SOUND (1U << 1)
+#define LOG_REGISTER_WRITE (1U << 2)
+#define LOG_REGISTER_READ (1U << 3)
+#define VERBOSE (0)
+#include "logmacro.h"
namespace {
@@ -116,7 +114,7 @@ void x1_010_device::device_start()
m_env_offset[i] = 0;
}
/* Print some more debug info */
- LOG_SOUND("masterclock = %d rate = %d\n", clock(), m_rate);
+ LOGMASKED(LOG_SOUND, "masterclock = %d rate = %d\n", clock(), m_rate);
/* get stream channels */
m_stream = stream_alloc(0, 2, m_rate);
@@ -147,10 +145,11 @@ void x1_010_device::device_clock_changed()
}
//-------------------------------------------------
-// rom_bank_updated - the rom bank has changed
+// rom_bank_pre_change - refresh the stream if the
+// ROM banking changes
//-------------------------------------------------
-void x1_010_device::rom_bank_updated()
+void x1_010_device::rom_bank_pre_change()
{
m_stream->update();
}
@@ -179,7 +178,7 @@ void x1_010_device::write(offs_t offset, u8 data)
m_smp_offset[channel] = 0;
m_env_offset[channel] = 0;
}
- LOG_REGISTER_WRITE("%s: offset %6X : data %2X\n", machine().describe_context(), offset, data);
+ LOGMASKED(LOG_REGISTER_WRITE, "%s: offset %6X : data %2X\n", machine().describe_context(), offset, data);
m_reg[offset] = data;
}
@@ -191,7 +190,7 @@ u16 x1_010_device::word_r(offs_t offset)
u16 ret;
ret = m_HI_WORD_BUF[offset] << 8;
ret |= (read(offset) & 0xff);
- LOG_REGISTER_READ("%s: Read X1-010 Offset:%04X Data:%04X\n", machine().describe_context(), offset, ret);
+ LOGMASKED(LOG_REGISTER_READ, "%s: Read X1-010 Offset:%04X Data:%04X\n", machine().describe_context(), offset, ret);
return ret;
}
@@ -199,7 +198,7 @@ void x1_010_device::word_w(offs_t offset, u16 data)
{
m_HI_WORD_BUF[offset] = (data >> 8) & 0xff;
write(offset, data & 0xff);
- LOG_REGISTER_WRITE("%s: Write X1-010 Offset:%04X Data:%04X\n", machine().describe_context(), offset, data);
+ LOGMASKED(LOG_REGISTER_WRITE, "%s: Write X1-010 Offset:%04X Data:%04X\n", machine().describe_context(), offset, data);
}
@@ -237,7 +236,7 @@ void x1_010_device::sound_stream_update(sound_stream &stream, std::vector<read_s
const u32 smp_step = freq;
if (smp_offs == 0)
{
- LOG_SOUND("Play sample %p - %p, channel %X volume %d:%d freq %X step %X offset %X\n",
+ LOGMASKED(LOG_SOUND, "Play sample %p - %p, channel %X volume %d:%d freq %X step %X offset %X\n",
start, end, ch, volL, volR, freq, smp_step, smp_offs);
}
for (int i = 0; i < bufL.samples(); i++)
@@ -269,7 +268,7 @@ void x1_010_device::sound_stream_update(sound_stream &stream, std::vector<read_s
/* Print some more debug info */
if (smp_offs == 0)
{
- LOG_SOUND("Play waveform %X, channel %X volume %X freq %4X step %X offset %X\n",
+ LOGMASKED(LOG_SOUND, "Play waveform %X, channel %X volume %X freq %4X step %X offset %X\n",
reg->volume, ch, reg->end, freq, smp_step, smp_offs);
}
for (int i = 0; i < bufL.samples(); i++)
diff --git a/src/devices/sound/x1_010.h b/src/devices/sound/x1_010.h
index b6b266b0ba0..a107534d4ae 100644
--- a/src/devices/sound/x1_010.h
+++ b/src/devices/sound/x1_010.h
@@ -29,7 +29,7 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
// device_rom_interface overrides
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
private:
static constexpr unsigned NUM_CHANNELS = 16;
diff --git a/src/devices/sound/xt446.cpp b/src/devices/sound/xt446.cpp
index 9dc3053f1ff..8136f43ed90 100644
--- a/src/devices/sound/xt446.cpp
+++ b/src/devices/sound/xt446.cpp
@@ -26,7 +26,7 @@ ROM_START( xt446 )
// MU-100B v1.08 (Nov. 28, 1997)
ROM_LOAD16_WORD_SWAP( "xu50710-m27c160.bin", 0x000000, 0x200000, CRC(4b10bd27) SHA1(12d7c6e1bce7974b34916e1bfa5057ab55867476) )
- ROM_REGION( 0x1800000, "swp30", ROMREGION_ERASE00 )
+ ROM_REGION32_LE( 0x1800000, "swp30", ROMREGION_ERASE00 )
ROM_LOAD32_WORD( "sx518b0.ic34", 0x0000000, 0x400000, CRC(2550d44f) SHA1(fd3cce228c7d389a2fde25c808a5b26080588cba) )
ROM_LOAD32_WORD( "sx743b0.ic35", 0x0000002, 0x400000, CRC(a9109a6c) SHA1(a67bb49378a38a2d809bd717d286e18bc6496db0) )
ROM_LOAD32_WORD( "xt445a0-828.ic36", 0x0800000, 0x200000, CRC(225c2280) SHA1(23b5e046fd2e2ac01af3e6dc6357c5c6547b286b) )
@@ -40,7 +40,6 @@ xt446_device::xt446_device(const machine_config &mconfig, const char *tag, devic
, device_mixer_interface(mconfig, *this, 2)
, m_maincpu(*this, "maincpu")
, m_swp30(*this, "swp30")
- , m_midi_serial(*this, "maincpu:sci1")
{
}
@@ -64,33 +63,34 @@ void xt446_device::xt446_map(address_map &map)
map(0x400000, 0x401fff).m(m_swp30, FUNC(swp30_device::map));
}
-void xt446_device::xt446_iomap(address_map &map)
-{
- map(h8_device::ADC_0, h8_device::ADC_0).lr16(NAME([]() -> u16 { return 0; }));
- map(h8_device::ADC_1, h8_device::ADC_1).lr16(NAME([]() -> u16 { return 0; }));
- map(h8_device::ADC_2, h8_device::ADC_2).lr16(NAME([]() -> u16 { return 0; }));
- map(h8_device::ADC_1, h8_device::ADC_3).lr16(NAME([]() -> u16 { return 0; }));
- map(h8_device::ADC_4, h8_device::ADC_4).lr16(NAME([]() -> u16 { return 0; }));
- map(h8_device::ADC_5, h8_device::ADC_5).lr16(NAME([]() -> u16 { return 0; }));
- map(h8_device::ADC_6, h8_device::ADC_6).lr16(NAME([]() -> u16 { return 0x200; }));
- map(h8_device::ADC_7, h8_device::ADC_7).lr16(NAME([]() -> u16 { return 0x200; }));
-}
-
void xt446_device::swp30_map(address_map &map)
{
- map(0x000000*4, 0x200000*4-1).rom().region("swp30", 0).mirror(4*0x200000);
- map(0x400000*4, 0x500000*4-1).rom().region("swp30", 0x800000).mirror(4*0x300000);
- map(0x800000*4, 0xa00000*4-1).rom().region("swp30", 0x1000000).mirror(4*0x200000);
+ map(0x000000, 0x1fffff).rom().region("swp30", 0).mirror(0x200000);
+ map(0x400000, 0x4fffff).rom().region("swp30", 0x800000).mirror(0x300000);
+ map(0x800000, 0x9fffff).rom().region("swp30", 0x1000000).mirror(0x200000);
}
void xt446_device::device_add_mconfig(machine_config &config)
{
H8S2655(config, m_maincpu, 16_MHz_XTAL);
m_maincpu->set_addrmap(AS_PROGRAM, &xt446_device::xt446_map);
- m_maincpu->set_addrmap(AS_IO, &xt446_device::xt446_iomap);
+ m_maincpu->read_adc<0>().set_constant(0);
+ m_maincpu->read_adc<1>().set_constant(0);
+ m_maincpu->read_adc<2>().set_constant(0);
+ m_maincpu->read_adc<3>().set_constant(0);
+ m_maincpu->read_adc<4>().set_constant(0);
+ m_maincpu->read_adc<5>().set_constant(0);
+ m_maincpu->read_adc<6>().set_constant(0x200);
+ m_maincpu->read_adc<7>().set_constant(0x3ff);
+ m_maincpu->read_port1().set_constant(0);
+ m_maincpu->write_port1().set_nop();
+ m_maincpu->write_port2().set_nop();
+ m_maincpu->read_port6().set_constant(0);
+ m_maincpu->read_porta().set_constant(0);
+ m_maincpu->write_portf().set_nop();
SWP30(config, m_swp30);
- m_swp30->set_addrmap(0, &xt446_device::swp30_map);
+ m_swp30->set_addrmap(AS_DATA, &xt446_device::swp30_map);
m_swp30->add_route(0, *this, 1.0, AUTO_ALLOC_INPUT, 0);
m_swp30->add_route(1, *this, 1.0, AUTO_ALLOC_INPUT, 1);
}
diff --git a/src/devices/sound/xt446.h b/src/devices/sound/xt446.h
index 5404dfd3466..e29c230402a 100644
--- a/src/devices/sound/xt446.h
+++ b/src/devices/sound/xt446.h
@@ -20,7 +20,7 @@ class xt446_device : public device_t, public device_mixer_interface
public:
xt446_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
- void midi_w(int state) { m_midi_serial->rx_w(state); }
+ void midi_w(int state) { m_maincpu->sci_rx_w<0>(state); }
protected:
virtual void device_start() override;
@@ -31,9 +31,7 @@ protected:
private:
required_device<h8s2655_device> m_maincpu;
required_device<swp30_device> m_swp30;
- required_device<h8_sci_device> m_midi_serial;
- void xt446_iomap(address_map &map);
void xt446_map(address_map &map);
void swp30_map(address_map &map);
};
diff --git a/src/devices/sound/ym2154.cpp b/src/devices/sound/ym2154.cpp
index 287529aeeec..b52c4e49532 100644
--- a/src/devices/sound/ym2154.cpp
+++ b/src/devices/sound/ym2154.cpp
@@ -21,7 +21,7 @@ ym2154_device::ym2154_device(const machine_config &mconfig, const char *tag, dev
m_stream(nullptr),
m_timer(nullptr),
m_update_irq(*this),
- m_io_read(*this),
+ m_io_read(*this, 0),
m_io_write(*this),
m_group0_config("group0", ENDIANNESS_LITTLE, 8, 18, 0),
m_group1_config("group1", ENDIANNESS_LITTLE, 8, 18, 0),
@@ -50,7 +50,7 @@ u8 ym2154_device::read(offs_t offset)
case 0x08: // A/D converter registers
case 0x09: // A/D converter registers
case 0x0a: // A/D converter registers
- result = m_io_read.isnull() ? 0 : m_io_read(offset - 1);
+ result = m_io_read(offset - 1);
break;
case 0x0e: // IRQ ack
@@ -90,8 +90,7 @@ void ym2154_device::write(offs_t offset, u8 data)
m_timer->enable(false);
else if (m_timer_enable && !old)
m_timer->adjust((2048 - m_timer_count) * attotime::from_hz(sample_rate()));
- if (!m_io_write.isnull())
- m_io_write(0, BIT(data, 4, 4) ^ 0x0f);
+ m_io_write(0, BIT(data, 4, 4) ^ 0x0f);
break;
// output level
@@ -179,11 +178,6 @@ void ym2154_device::device_start()
// allocate our timer
m_timer = timer_alloc(FUNC(ym2154_device::delayed_irq), this);
- // resolve the handlers
- m_update_irq.resolve();
- m_io_read.resolve();
- m_io_write.resolve();
-
// allocate our stream
m_stream = stream_alloc(0, 2, sample_rate());
diff --git a/src/devices/sound/ymf271.cpp b/src/devices/sound/ymf271.cpp
index b8240df9c72..3f748656d91 100644
--- a/src/devices/sound/ymf271.cpp
+++ b/src/devices/sound/ymf271.cpp
@@ -1317,9 +1317,7 @@ TIMER_CALLBACK_MEMBER(ymf271_device::timer_a_expired)
if (m_enable & 4)
{
m_irqstate |= 1;
-
- if (!m_irq_handler.isnull())
- m_irq_handler(1);
+ m_irq_handler(1);
}
// reload timer
@@ -1334,9 +1332,7 @@ TIMER_CALLBACK_MEMBER(ymf271_device::timer_b_expired)
if (m_enable & 8)
{
m_irqstate |= 2;
-
- if (!m_irq_handler.isnull())
- m_irq_handler(1);
+ m_irq_handler(1);
}
// reload timer
@@ -1397,7 +1393,7 @@ void ymf271_device::ymf271_write_timer(uint8_t address, uint8_t data)
m_irqstate &= ~1;
m_status &= ~1;
- if (!m_irq_handler.isnull() && ~m_irqstate & 2)
+ if (~m_irqstate & 2)
m_irq_handler(0);
}
@@ -1407,7 +1403,7 @@ void ymf271_device::ymf271_write_timer(uint8_t address, uint8_t data)
m_irqstate &= ~2;
m_status &= ~2;
- if (!m_irq_handler.isnull() && ~m_irqstate & 1)
+ if (~m_irqstate & 1)
m_irq_handler(0);
}
@@ -1727,8 +1723,6 @@ void ymf271_device::device_start()
m_timA = timer_alloc(FUNC(ymf271_device::timer_a_expired), this);
m_timB = timer_alloc(FUNC(ymf271_device::timer_b_expired), this);
- m_irq_handler.resolve();
-
m_master_clock = clock();
init_tables();
init_state();
@@ -1757,8 +1751,7 @@ void ymf271_device::device_reset()
m_status = 0;
m_enable = 0;
- if (!m_irq_handler.isnull())
- m_irq_handler(0);
+ m_irq_handler(0);
}
//-------------------------------------------------
@@ -1781,7 +1774,7 @@ void ymf271_device::device_clock_changed()
calculate_clock_correction();
}
-void ymf271_device::rom_bank_updated()
+void ymf271_device::rom_bank_pre_change()
{
m_stream->update();
}
diff --git a/src/devices/sound/ymf271.h b/src/devices/sound/ymf271.h
index 5c6e432649b..04d49c57f09 100644
--- a/src/devices/sound/ymf271.h
+++ b/src/devices/sound/ymf271.h
@@ -30,7 +30,7 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
// device_rom_interface overrides
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
TIMER_CALLBACK_MEMBER(timer_a_expired);
TIMER_CALLBACK_MEMBER(timer_b_expired);
diff --git a/src/devices/sound/ymfm_mame.h b/src/devices/sound/ymfm_mame.h
index d0161426619..e6400e76d6c 100644
--- a/src/devices/sound/ymfm_mame.h
+++ b/src/devices/sound/ymfm_mame.h
@@ -35,8 +35,8 @@ public:
device_sound_interface(mconfig, *this),
m_timer{ nullptr, nullptr },
m_update_irq(*this),
- m_io_read{ *this, *this },
- m_io_write{ *this, *this }
+ m_io_read(*this, 0),
+ m_io_write(*this)
{
}
@@ -92,12 +92,11 @@ protected:
}
// the chip implementation calls this when the state of the IRQ signal has
- // changed due to a status change; our responsibility is to respons as
+ // changed due to a status change; our responsibility is to response as
// needed to the change in IRQ state, signaling any consumers
virtual void ymfm_update_irq(bool asserted) override
{
- if (!m_update_irq.isnull())
- m_update_irq(asserted ? ASSERT_LINE : CLEAR_LINE);
+ m_update_irq(asserted ? ASSERT_LINE : CLEAR_LINE);
}
// the chip implementation calls this to indicate that the chip should be
@@ -122,14 +121,14 @@ protected:
// of the chip; our responsibility is to provide the data requested
virtual uint8_t ymfm_external_read(ymfm::access_class type, uint32_t address) override
{
- return (type != ymfm::ACCESS_IO || m_io_read[address & 1].isnull()) ? 0 : m_io_read[address & 1]();
+ return (type != ymfm::ACCESS_IO) ? 0 : m_io_read[address & 1]();
}
// the chip implementation calls this whenever data is written outside
// of the chip; our responsibility is to pass the written data on to any consumers
virtual void ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data) override
{
- if (type == ymfm::ACCESS_IO && !m_io_write[address & 1].isnull())
+ if (type == ymfm::ACCESS_IO)
m_io_write[address & 1](data);
}
@@ -140,28 +139,21 @@ protected:
for (int tnum = 0; tnum < 2; tnum++)
m_timer[tnum] = timer_alloc(FUNC(ym_generic_device::fm_timer_handler), this);
- // resolve the handlers
- m_update_irq.resolve();
- m_io_read[0].resolve();
- m_io_read[1].resolve();
- m_io_write[0].resolve();
- m_io_write[1].resolve();
-
// remember the busy end time
save_item(NAME(m_busy_end));
}
// timer callbacks
- void fm_mode_write(int param) { m_engine->engine_mode_write(param); }
- void fm_check_interrupts(int param) { m_engine->engine_check_interrupts(); }
- void fm_timer_handler(int param) { m_engine->engine_timer_expired(param); }
+ void fm_mode_write(s32 param) { m_engine->engine_mode_write(param); }
+ void fm_check_interrupts(s32 param) { m_engine->engine_check_interrupts(); }
+ void fm_timer_handler(s32 param) { m_engine->engine_timer_expired(param); }
// internal state
- attotime m_busy_end; // busy end time
- emu_timer *m_timer[2]; // two timers
- devcb_write_line m_update_irq; // IRQ update callback
- devcb_read8 m_io_read[2]; // up to 2 input port handlers
- devcb_write8 m_io_write[2]; // up to 2 output port handlers
+ attotime m_busy_end; // busy end time
+ emu_timer *m_timer[2]; // two timers
+ devcb_write_line m_update_irq; // IRQ update callback
+ devcb_read8::array<2> m_io_read; // up to 2 input port handlers
+ devcb_write8::array<2> m_io_write; // up to 2 output port handlers
};
diff --git a/src/devices/sound/ymopl.cpp b/src/devices/sound/ymopl.cpp
index 352906f40b8..92e9eb99928 100644
--- a/src/devices/sound/ymopl.cpp
+++ b/src/devices/sound/ymopl.cpp
@@ -40,11 +40,11 @@ y8950_device::y8950_device(const machine_config &mconfig, const char *tag, devic
//-------------------------------------------------
-// rom_bank_updated - refresh the stream if the
+// rom_bank_pre_change - refresh the stream if the
// ROM banking changes
//-------------------------------------------------
-void y8950_device::rom_bank_updated()
+void y8950_device::rom_bank_pre_change()
{
m_stream->update();
}
@@ -131,11 +131,11 @@ ymf278b_device::ymf278b_device(const machine_config &mconfig, const char *tag, d
//-------------------------------------------------
-// rom_bank_updated - refresh the stream if the
+// rom_bank_pre_change - refresh the stream if the
// ROM banking changes
//-------------------------------------------------
-void ymf278b_device::rom_bank_updated()
+void ymf278b_device::rom_bank_pre_change()
{
m_stream->update();
}
diff --git a/src/devices/sound/ymopl.h b/src/devices/sound/ymopl.h
index 07ae7ead5cb..6d5f279be03 100644
--- a/src/devices/sound/ymopl.h
+++ b/src/devices/sound/ymopl.h
@@ -46,7 +46,7 @@ public:
protected:
// ROM device overrides
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
private:
// ADPCM read/write callbacks
@@ -106,7 +106,7 @@ public:
protected:
// device_rom_interface overrides
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
// sound overrides
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
diff --git a/src/devices/sound/ymopm.cpp b/src/devices/sound/ymopm.cpp
index a75da72b449..0e401287304 100644
--- a/src/devices/sound/ymopm.cpp
+++ b/src/devices/sound/ymopm.cpp
@@ -52,7 +52,7 @@ void ym2151_device::data_w(u8 data)
// reset_w - reset line, active LOW
//-------------------------------------------------
-WRITE_LINE_MEMBER(ym2151_device::reset_w)
+void ym2151_device::reset_w(int state)
{
if (state != m_reset_state)
{
diff --git a/src/devices/sound/ymopm.h b/src/devices/sound/ymopm.h
index ce4a7c87f51..da4c46afa98 100644
--- a/src/devices/sound/ymopm.h
+++ b/src/devices/sound/ymopm.h
@@ -31,7 +31,7 @@ public:
virtual void data_w(u8 data) override;
// reset line, active LOW
- DECLARE_WRITE_LINE_MEMBER(reset_w);
+ void reset_w(int state);
protected:
// internal state
diff --git a/src/devices/sound/ymopn.cpp b/src/devices/sound/ymopn.cpp
index 4f71646ad1d..de18330fa89 100644
--- a/src/devices/sound/ymopn.cpp
+++ b/src/devices/sound/ymopn.cpp
@@ -98,11 +98,11 @@ const tiny_rom_entry *ym2608_device::device_rom_region() const
//-------------------------------------------------
-// rom_bank_updated - refresh the stream if the
+// rom_bank_pre_change - refresh the stream if the
// ROM banking changes
//-------------------------------------------------
-void ym2608_device::rom_bank_updated()
+void ym2608_device::rom_bank_pre_change()
{
m_stream->update();
}
diff --git a/src/devices/sound/ymopn.h b/src/devices/sound/ymopn.h
index c65738dee07..93b0999be8c 100644
--- a/src/devices/sound/ymopn.h
+++ b/src/devices/sound/ymopn.h
@@ -71,7 +71,7 @@ protected:
virtual const tiny_rom_entry *device_rom_region() const override;
// ROM device overrides
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
private:
// ADPCM read/write callbacks
diff --git a/src/devices/sound/ymz280b.cpp b/src/devices/sound/ymz280b.cpp
index 319b03a381b..8eced2f7fa8 100644
--- a/src/devices/sound/ymz280b.cpp
+++ b/src/devices/sound/ymz280b.cpp
@@ -67,16 +67,18 @@ void ymz280b_device::update_irq_state()
if (irq_bits && !m_irq_state)
{
m_irq_state = 1;
- if (!m_irq_handler.isnull())
+ if (!m_irq_handler.isunset())
m_irq_handler(1);
- else logerror("YMZ280B: IRQ generated, but no callback specified!\n");
+ else
+ logerror("YMZ280B: IRQ generated, but no callback specified!\n");
}
else if (!irq_bits && m_irq_state)
{
m_irq_state = 0;
- if (!m_irq_handler.isnull())
+ if (!m_irq_handler.isunset())
m_irq_handler(0);
- else logerror("YMZ280B: IRQ generated, but no callback specified!\n");
+ else
+ logerror("YMZ280B: IRQ generated, but no callback specified!\n");
}
}
@@ -552,7 +554,6 @@ void ymz280b_device::device_start()
/* initialize the rest of the structure */
m_master_clock = (double)clock() / 384.0;
- m_irq_handler.resolve();
for (int i = 0; i < 8; i++)
{
@@ -646,7 +647,7 @@ void ymz280b_device::device_clock_changed()
}
-void ymz280b_device::rom_bank_updated()
+void ymz280b_device::rom_bank_pre_change()
{
m_stream->update();
}
diff --git a/src/devices/sound/ymz280b.h b/src/devices/sound/ymz280b.h
index 04b229de4d3..319f85b83f4 100644
--- a/src/devices/sound/ymz280b.h
+++ b/src/devices/sound/ymz280b.h
@@ -38,7 +38,7 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
// device_rom_interface overrides
- virtual void rom_bank_updated() override;
+ virtual void rom_bank_pre_change() override;
private:
/* struct describing a single playing ADPCM voice */
diff --git a/src/devices/sound/zsg2.cpp b/src/devices/sound/zsg2.cpp
index 518d765ccc3..66072775500 100644
--- a/src/devices/sound/zsg2.cpp
+++ b/src/devices/sound/zsg2.cpp
@@ -120,7 +120,7 @@ zsg2_device::zsg2_device(const machine_config &mconfig, const char *tag, device_
, device_sound_interface(mconfig, *this)
, m_mem_base(*this, DEVICE_SELF)
, m_read_address(0)
- , m_ext_read_handler(*this)
+ , m_ext_read_handler(*this, 0)
{
}
@@ -130,8 +130,6 @@ zsg2_device::zsg2_device(const machine_config &mconfig, const char *tag, device_
void zsg2_device::device_start()
{
- m_ext_read_handler.resolve();
-
memset(&m_chan, 0, sizeof(m_chan));
m_stream = stream_alloc(0, 4, clock() / 768);
@@ -227,7 +225,7 @@ uint32_t zsg2_device::read_memory(uint32_t offset)
if (offset >= m_mem_blocks)
return 0;
- if (m_ext_read_handler.isnull())
+ if (m_ext_read_handler.isunset())
return m_mem_base[offset];
return m_ext_read_handler(offset);