summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/audio
diff options
context:
space:
mode:
author AJR <ajrhacker@users.noreply.github.com>2018-04-27 16:50:50 -0400
committer AJR <ajrhacker@users.noreply.github.com>2018-04-27 16:50:50 -0400
commited8bb86e08d1fcbd93d603af131af8a5164d60e6 (patch)
tree7b5ee52a60bb56effd245592e2644c945fdca21a /src/mame/audio
parentac7b891ec8ccd55262abb071d315deb9a62caa63 (diff)
cmi.cpp: Separate source file for CMI-01A device (nw)
Diffstat (limited to 'src/mame/audio')
-rw-r--r--src/mame/audio/cmi01a.cpp422
-rw-r--r--src/mame/audio/cmi01a.h93
2 files changed, 515 insertions, 0 deletions
diff --git a/src/mame/audio/cmi01a.cpp b/src/mame/audio/cmi01a.cpp
new file mode 100644
index 00000000000..26d4e5a4430
--- /dev/null
+++ b/src/mame/audio/cmi01a.cpp
@@ -0,0 +1,422 @@
+// license:BSD-3-Clause
+// copyright-holders:Phil Bennett
+/***************************************************************************
+
+ Fairlight CMI-01A Channel Controller Card
+
+***************************************************************************/
+
+#include "emu.h"
+#include "audio/cmi01a.h"
+#include "machine/input_merger.h"
+
+#define MASTER_OSCILLATOR XTAL(34'291'712)
+
+
+DEFINE_DEVICE_TYPE(CMI01A_CHANNEL_CARD, cmi01a_device, "cmi_01a", "Fairlight CMI-01A Channel Card")
+
+cmi01a_device::cmi01a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, CMI01A_CHANNEL_CARD, tag, owner, clock)
+ , device_sound_interface(mconfig, *this)
+ , m_pia(*this, "cmi01a_pia_%u", 0U)
+ , m_ptm(*this, "cmi01a_ptm")
+ , m_cmi02_pia(*this, "^cmi02_pia_%u", 1U)
+ , m_stream(nullptr)
+ , m_irq_cb(*this)
+{
+}
+
+MACHINE_CONFIG_START(cmi01a_device::device_add_mconfig)
+ MCFG_DEVICE_ADD("cmi01a_pia_0", PIA6821, 0) // pia_cmi01a_1_config
+ MCFG_PIA_READCB1_HANDLER(READLINE(cmi01a_device, tri_r))
+ MCFG_PIA_WRITEPA_HANDLER(WRITE8(cmi01a_device, ws_dir_w))
+ MCFG_PIA_WRITEPB_HANDLER(WRITE8(cmi01a_device, rp_w))
+ MCFG_PIA_CA2_HANDLER(WRITELINE(cmi01a_device, pia_0_ca2_w))
+ MCFG_PIA_CB2_HANDLER(WRITELINE(cmi01a_device, pia_0_cb2_w))
+ MCFG_PIA_IRQA_HANDLER(DEVWRITELINE("cmi01a_irq", input_merger_device, in_w<0>))
+ MCFG_PIA_IRQB_HANDLER(DEVWRITELINE("cmi01a_irq", input_merger_device, in_w<1>))
+
+ MCFG_DEVICE_ADD("cmi01a_pia_1", PIA6821, 0) // pia_cmi01a_2_config
+ MCFG_PIA_READCA1_HANDLER(READLINE(cmi01a_device, zx_r))
+ MCFG_PIA_READCA2_HANDLER(READLINE(cmi01a_device, eosi_r))
+ MCFG_PIA_WRITEPA_HANDLER(WRITE8(cmi01a_device, pia_1_a_w))
+ MCFG_PIA_WRITEPB_HANDLER(WRITE8(cmi01a_device, pia_1_b_w))
+ MCFG_PIA_IRQA_HANDLER(DEVWRITELINE("cmi01a_irq", input_merger_device, in_w<2>))
+ MCFG_PIA_IRQB_HANDLER(DEVWRITELINE("cmi01a_irq", input_merger_device, in_w<3>))
+
+ MCFG_DEVICE_ADD("cmi01a_ptm", PTM6840, 2000000) // ptm_cmi01a_config
+ MCFG_PTM6840_EXTERNAL_CLOCKS(250000, 500000, 500000)
+ MCFG_PTM6840_O1_CB(WRITELINE(cmi01a_device, ptm_o1))
+ MCFG_PTM6840_IRQ_CB(DEVWRITELINE("cmi01a_irq", input_merger_device, in_w<4>))
+
+ MCFG_INPUT_MERGER_ANY_HIGH("cmi01a_irq")
+ MCFG_INPUT_MERGER_OUTPUT_HANDLER(WRITELINE(cmi01a_device, cmi01a_irq))
+MACHINE_CONFIG_END
+
+
+void cmi01a_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ if (m_active && m_vol_latch)
+ {
+ int length = samples;
+ int seg_addr = m_segment_cnt & 0x7f;
+ uint8_t *wave_ptr = &m_wave_ram[m_segment_cnt & 0x3fff];
+ stream_sample_t *buf = outputs[0];
+
+ while (length--)
+ {
+ *buf++ = wave_ptr[seg_addr];
+ seg_addr = (seg_addr + 1) & 0x7f;
+ }
+
+ m_segment_cnt = (m_segment_cnt & ~0x7f) | seg_addr;
+ }
+ else
+ {
+ memset(outputs[0], 0, samples);
+ }
+}
+
+void cmi01a_device::device_resolve_objects()
+{
+ m_irq_cb.resolve_safe();
+}
+
+void cmi01a_device::device_start()
+{
+ m_wave_ram = std::make_unique<uint8_t[]>(0x4000);
+
+ m_zx_timer = timer_alloc(TIMER_ZX);
+ m_zx_timer->adjust(attotime::never);
+
+ m_stream = stream_alloc(0, 1, 44100);
+}
+
+void cmi01a_device::device_reset()
+{
+ m_ptm->set_g1(1);
+ m_ptm->set_g2(1);
+ m_ptm->set_g3(1);
+
+ m_segment_cnt = 0;
+ m_new_addr = 0;
+ m_env_dir_ctrl = 0;
+ m_vol_latch = 0;
+ m_flt_latch = 0;
+ m_rp = 0;
+ m_ws = 0;
+ m_dir = 0;
+
+ m_freq = 0.0;
+ m_active = false;
+
+ m_ptm_o1 = 0;
+}
+
+WRITE_LINE_MEMBER( cmi01a_device::pia_0_ca2_w )
+{
+ // upate_stream()
+ if (!state)
+ {
+ m_segment_cnt = 0x4000 | ((m_pia[0]->a_output() & 0x7f) << 7);
+ m_new_addr = 1;
+ m_pia[1]->cb1_w(1);
+ }
+}
+
+WRITE8_MEMBER( cmi01a_device::pia_1_a_w )
+{
+// top two
+}
+
+WRITE8_MEMBER( cmi01a_device::pia_1_b_w )
+{
+}
+
+WRITE8_MEMBER( cmi01a_device::rp_w )
+{
+ m_rp = data;
+}
+
+WRITE8_MEMBER( cmi01a_device::ws_dir_w )
+{
+ m_ws = data & 0x7f;
+ m_dir = (data >> 7) & 1;
+}
+
+READ_LINE_MEMBER( cmi01a_device::tri_r )
+{
+ bool top_terminal_count = (m_dir == ENV_DIR_UP && m_rp == 0);
+ bool bottom_terminal_count = (m_dir == ENV_DIR_DOWN && m_rp == 0xff);
+ return (top_terminal_count || bottom_terminal_count) ? 1 : 0;
+}
+
+WRITE_LINE_MEMBER( cmi01a_device::cmi01a_irq )
+{
+ m_irq_cb(state ? ASSERT_LINE : CLEAR_LINE);
+}
+
+void cmi01a_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch(id)
+ {
+ case TIMER_ZX:
+ zx_timer_cb();
+ break;
+ }
+}
+
+void cmi01a_device::zx_timer_cb()
+{
+ /* Set ZX */
+ if (m_zx_flag == 0)
+ m_pia[1]->ca1_w(1);
+ else
+ m_pia[1]->ca1_w(0);
+
+ m_zx_flag ^= 1;
+
+ if (m_zx_flag == 0)
+ {
+ /* Low to high transition - clock flip flop */
+ int op = m_ptm_o1;
+
+ /* Set /ZCINT */
+ if (op != m_zx_ff)
+ m_pia[0]->ca1_w(0);
+
+ m_zx_ff = op;
+ m_pia[0]->ca1_w(1);
+ }
+}
+
+void cmi01a_device::run_voice()
+{
+ int val_a = m_pia[1]->a_output();
+ int pitch = ((val_a & 3) << 8) | m_pia[1]->b_output();
+ int o_val = (val_a >> 2) & 0xf;
+
+ int m_tune = m_cmi02_pia[0]->b_output();
+ double mfreq = (double)(0xf00 | m_tune) * (MASTER_OSCILLATOR / 2.0 / 4096.0).dvalue();
+
+ double cfreq = ((double)(0x800 | (pitch << 1))* mfreq) / 4096.0;
+
+// if (cfreq > 0.0)
+ {
+ /* Octave register enabled? */
+ if (!(o_val & 0x8))
+ cfreq /= 2 << ((7 ^ o_val) & 7);
+
+ cfreq /= 16.0f;
+
+ m_freq = cfreq;
+
+ m_stream->set_sample_rate(cfreq);
+
+ // Set timers and things?
+ attotime zx_period = attotime::from_ticks(64, cfreq);
+ m_zx_timer->adjust(zx_period, 0, zx_period);
+
+ m_active = true;
+ }
+}
+
+WRITE_LINE_MEMBER( cmi01a_device::pia_0_cb2_w )
+{
+ //streams_update();
+
+ /* RUN */
+ if (state)
+ {
+ m_segment_cnt = 0x4000 | ((m_pia[0]->a_output() & 0x7f) << 7);
+ m_new_addr = 1;
+
+ /* Clear /EOSI */
+// pia6821_cb1_w(card->pia[1], 0, 1);
+
+ /* Clear ZX */
+ m_pia[1]->ca1_w(0);
+
+ /* Clear /ZCINT */
+ m_pia[0]->ca1_w(1);
+
+ m_ptm->set_g1(0);
+ m_ptm->set_g2(0);
+ m_ptm->set_g3(0);
+
+ run_voice();
+ }
+ else
+ {
+ /* Clear /EOSI */
+ m_pia[1]->cb1_w(1);
+
+ m_ptm->set_g1(1);
+ m_ptm->set_g2(1);
+ m_ptm->set_g3(1);
+
+ //printf("Stop %d\n", m_channel);
+
+ m_zx_timer->adjust(attotime::never);
+ m_active = false;
+ m_zx_flag = 0; // TEST
+ m_zx_ff = 0;
+ }
+
+}
+
+void cmi01a_device::update_wave_addr(int inc)
+{
+ int old_cnt = m_segment_cnt;
+
+ if (inc)
+ ++m_segment_cnt;
+
+ /* Update end of sound interrupt flag */
+ m_pia[1]->cb1_w((m_segment_cnt & 0x4000) >> 14);
+
+ /* TODO Update zero crossing flag */
+ m_pia[1]->ca1_w((m_segment_cnt & 0x40) >> 6);
+
+ /* Clock a latch on a transition */
+ if ((old_cnt & 0x40) && !(m_segment_cnt & 0x40))
+ {
+ // TODO: ECLK
+ m_pia[1]->ca2_w(1);
+ m_pia[1]->ca2_w(0);
+ }
+
+ /* Zero crossing interrupt is a pulse */
+}
+
+WRITE_LINE_MEMBER( cmi01a_device::ptm_o1 )
+{
+ m_ptm_o1 = state;
+}
+
+READ_LINE_MEMBER( cmi01a_device::eosi_r )
+{
+ return (m_segment_cnt & 0x4000) >> 14;
+}
+
+READ_LINE_MEMBER( cmi01a_device::zx_r )
+{
+ return m_segment_cnt & 0x40;
+}
+
+WRITE8_MEMBER( cmi01a_device::write )
+{
+ //printf("C%d W: %02x = %02x\n", m_channel, offset, data);
+
+ switch (offset)
+ {
+ case 0x0:
+ if (m_new_addr)
+ m_new_addr = 0;
+
+ m_wave_ram[m_segment_cnt & 0x3fff] = data;
+ update_wave_addr(1);
+ break;
+
+ case 0x3:
+ m_env_dir_ctrl = ENV_DIR_DOWN;
+ break;
+
+ case 0x4:
+ m_env_dir_ctrl = ENV_DIR_UP;
+ break;
+
+ case 0x5:
+ m_vol_latch = data;
+ break;
+
+ case 0x6:
+ m_flt_latch = data;
+ break;
+
+ case 0x8: case 0x9: case 0xa: case 0xb:
+ m_pia[0]->write(space, offset & 3, data);
+ break;
+
+ case 0xc: case 0xd: case 0xe: case 0xf:
+ m_pia[1]->write(space, (BIT(offset, 0) << 1) | BIT(offset, 1), data);
+ break;
+
+ case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17:
+ {
+ /* PTM addressing is a little funky */
+ int a0 = offset & 1;
+ int a1 = (m_ptm_o1 && BIT(offset, 3)) || (!BIT(offset, 3) && BIT(offset, 2));
+ int a2 = BIT(offset, 1);
+
+ //printf("CH%d PTM W: [%x] = %02x\n", m_channel, (a2 << 2) | (a1 << 1) | a0, data);
+ m_ptm->write(space, (a2 << 2) | (a1 << 1) | a0, data);
+ break;
+ }
+
+ default:
+ logerror("Unknown channel card write to E0%02X = %02X\n", offset, data);
+ break;
+ }
+}
+
+READ8_MEMBER( cmi01a_device::read )
+{
+ if (machine().side_effects_disabled())
+ return 0;
+
+ uint8_t data = 0;
+
+ switch (offset)
+ {
+ case 0x0:
+ if (m_new_addr)
+ {
+ m_new_addr = 0;
+ break;
+ }
+ data = m_wave_ram[m_segment_cnt & 0x3fff];
+ update_wave_addr(1);
+ break;
+
+ case 0x3:
+ m_env_dir_ctrl = ENV_DIR_DOWN;
+ break;
+
+ case 0x4:
+ m_env_dir_ctrl = ENV_DIR_UP;
+ break;
+
+ case 0x5:
+ data = 0xff;
+ break;
+
+ case 0x8: case 0x9: case 0xa: case 0xb:
+ data = m_pia[0]->read(space, offset & 3);
+ break;
+
+ case 0xc: case 0xd: case 0xe: case 0xf:
+ data = m_pia[1]->read(space, (BIT(offset, 0) << 1) | BIT(offset, 1));
+ break;
+
+ case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17:
+ {
+ int a0 = offset & 1;
+ int a1 = (m_ptm_o1 && BIT(offset, 3)) || (!BIT(offset, 3) && BIT(offset, 2));
+ int a2 = BIT(offset, 1);
+
+ data = m_ptm->read(space, (a2 << 2) | (a1 << 1) | a0);
+
+ //printf("CH%d PTM R: [%x] %02x\n", m_channel, (a2 << 2) | (a1 << 1) | a0, data);
+ break;
+ }
+
+ default:
+ logerror("Unknown channel card read from E0%02X\n", offset);
+ break;
+ }
+
+ //printf("C%d R: %02x = %02x\n", m_channel, offset, data);
+
+ return data;
+}
diff --git a/src/mame/audio/cmi01a.h b/src/mame/audio/cmi01a.h
new file mode 100644
index 00000000000..5f9bbd9f15f
--- /dev/null
+++ b/src/mame/audio/cmi01a.h
@@ -0,0 +1,93 @@
+// license:BSD-3-Clause
+// copyright-holders:Phil Bennett
+/***************************************************************************
+
+ Fairlight CMI-01A Channel Controller Card
+
+***************************************************************************/
+
+#ifndef MAME_AUDIO_CMI01A_H
+#define MAME_AUDIO_CMI01A_H
+
+#include "machine/6821pia.h"
+#include "machine/6840ptm.h"
+
+#define ENV_DIR_DOWN 0
+#define ENV_DIR_UP 1
+
+#define MCFG_CMI01A_IRQ_CALLBACK(_devcb) \
+ devcb = &downcast<cmi01a_device &>(*device).set_irq_callback(DEVCB_##_devcb);
+
+class cmi01a_device : public device_t, public device_sound_interface {
+public:
+ cmi01a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ template<class Object> devcb_base &set_irq_callback(Object &&cb) { return m_irq_cb.set_callback(std::forward<Object>(cb)); }
+
+ DECLARE_WRITE8_MEMBER( write );
+ DECLARE_READ8_MEMBER( read );
+
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override;
+
+protected:
+ virtual void device_resolve_objects() override;
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_add_mconfig(machine_config &config) override;
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
+
+ static const device_timer_id TIMER_ZX = 0;
+
+ required_device_array<pia6821_device, 2> m_pia;
+ required_device<ptm6840_device> m_ptm;
+
+ required_device_array<pia6821_device, 2> m_cmi02_pia;
+
+ sound_stream* m_stream;
+
+private:
+ DECLARE_WRITE_LINE_MEMBER(cmi01a_irq);
+
+ void zx_timer_cb();
+ void run_voice();
+ void update_wave_addr(int inc);
+
+ emu_timer * m_zx_timer;
+ uint8_t m_zx_flag;
+ uint8_t m_zx_ff;
+
+ std::unique_ptr<uint8_t[]> m_wave_ram;
+ uint16_t m_segment_cnt;
+ uint8_t m_new_addr; // Flag
+ uint8_t m_env_dir_ctrl;
+ uint8_t m_vol_latch;
+ uint8_t m_flt_latch;
+ uint8_t m_rp;
+ uint8_t m_ws;
+ int m_dir;
+
+ double m_freq;
+ bool m_active;
+
+ int m_ptm_o1;
+
+ devcb_write_line m_irq_cb;
+
+ DECLARE_WRITE8_MEMBER( rp_w );
+ DECLARE_WRITE8_MEMBER( ws_dir_w );
+ DECLARE_READ_LINE_MEMBER( tri_r );
+ DECLARE_WRITE_LINE_MEMBER( pia_0_ca2_w );
+ DECLARE_WRITE_LINE_MEMBER( pia_0_cb2_w );
+
+ DECLARE_READ_LINE_MEMBER( eosi_r );
+ DECLARE_READ_LINE_MEMBER( zx_r );
+ DECLARE_WRITE8_MEMBER( pia_1_a_w );
+ DECLARE_WRITE8_MEMBER( pia_1_b_w );
+
+ DECLARE_WRITE_LINE_MEMBER( ptm_o1 );
+};
+
+// device type definition
+DECLARE_DEVICE_TYPE(CMI01A_CHANNEL_CARD, cmi01a_device)
+
+#endif // MAME_AUDIO_CMI01A_H