From f6554879ce7b7802f6e1312531775642f8a279ea Mon Sep 17 00:00:00 2001 From: AJR Date: Sat, 14 Apr 2018 17:53:55 -0400 Subject: z80ctc: Make channels into subdevices --- src/devices/machine/z80ctc.cpp | 234 ++++++++++++++++++++--------------------- src/devices/machine/z80ctc.h | 96 +++++++++-------- 2 files changed, 165 insertions(+), 165 deletions(-) diff --git a/src/devices/machine/z80ctc.cpp b/src/devices/machine/z80ctc.cpp index efc8eae1aea..a28991e021f 100644 --- a/src/devices/machine/z80ctc.cpp +++ b/src/devices/machine/z80ctc.cpp @@ -19,9 +19,7 @@ //************************************************************************** #define VERBOSE 0 - -#define VPRINTF(x) do { if (VERBOSE) logerror x; } while (0) -#define VPRINTF_CHANNEL(x) do { if (VERBOSE) m_device->logerror x; } while (0) +#include "logmacro.h" @@ -30,40 +28,40 @@ //************************************************************************** // these are the bits of the incoming commands to the CTC -const int INTERRUPT = 0x80; -const int INTERRUPT_ON = 0x80; -//const int INTERRUPT_OFF = 0x00; +constexpr u16 INTERRUPT = 0x80; +constexpr u16 INTERRUPT_ON = 0x80; +//constexpr u16 INTERRUPT_OFF = 0x00; -const int MODE = 0x40; -const int MODE_TIMER = 0x00; -const int MODE_COUNTER = 0x40; +constexpr u16 MODE = 0x40; +constexpr u16 MODE_TIMER = 0x00; +constexpr u16 MODE_COUNTER = 0x40; -const int PRESCALER = 0x20; -//const int PRESCALER_256 = 0x20; -const int PRESCALER_16 = 0x00; +constexpr u16 PRESCALER = 0x20; +//constexpr u16 PRESCALER_256 = 0x20; +constexpr u16 PRESCALER_16 = 0x00; -const int EDGE = 0x10; -const int EDGE_FALLING = 0x00; -const int EDGE_RISING = 0x10; +constexpr u16 EDGE = 0x10; +constexpr u16 EDGE_FALLING = 0x00; +constexpr u16 EDGE_RISING = 0x10; -const int TRIGGER = 0x08; -const int TRIGGER_AUTO = 0x00; -//const int TRIGGER_CLOCK = 0x08; +constexpr u16 TRIGGER = 0x08; +constexpr u16 TRIGGER_AUTO = 0x00; +//constexpr u16 TRIGGER_CLOCK = 0x08; -const int CONSTANT = 0x04; -const int CONSTANT_LOAD = 0x04; -//const int CONSTANT_NONE = 0x00; +constexpr u16 CONSTANT = 0x04; +constexpr u16 CONSTANT_LOAD = 0x04; +//constexpr u16 CONSTANT_NONE = 0x00; -const int RESET = 0x02; -//const int RESET_CONTINUE = 0x00; -const int RESET_ACTIVE = 0x02; +constexpr u16 RESET = 0x02; +//constexpr u16 RESET_CONTINUE = 0x00; +constexpr u16 RESET_ACTIVE = 0x02; -const int CONTROL = 0x01; -const int CONTROL_VECTOR = 0x00; -const int CONTROL_WORD = 0x01; +constexpr u16 CONTROL = 0x01; +constexpr u16 CONTROL_VECTOR = 0x00; +constexpr u16 CONTROL_WORD = 0x01; // these extra bits help us keep things accurate -const int WAITING_FOR_TRIG = 0x100; +constexpr u16 WAITING_FOR_TRIG = 0x100; @@ -71,22 +69,21 @@ const int WAITING_FOR_TRIG = 0x100; // LIVE DEVICE //************************************************************************** -// device type definition +// device type definitions DEFINE_DEVICE_TYPE(Z80CTC, z80ctc_device, "z80ctc", "Z80 CTC") +DEFINE_DEVICE_TYPE(Z80CTC_CHANNEL, z80ctc_channel_device, "z80ctc_channel", "Z80 CTC Channel") //------------------------------------------------- // z80ctc_device - constructor //------------------------------------------------- -z80ctc_device::z80ctc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) +z80ctc_device::z80ctc_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : device_t(mconfig, Z80CTC, tag, owner, clock), device_z80daisy_interface(mconfig, *this), m_intr_cb(*this), - m_zc0_cb(*this), - m_zc1_cb(*this), - m_zc2_cb(*this), - m_zc3_cb(*this), - m_vector(0) + m_zc_cb{*this, *this, *this, *this}, + m_vector(0), + m_channel(*this, "ch%u", 0U) { } @@ -97,7 +94,7 @@ z80ctc_device::z80ctc_device(const machine_config &mconfig, const char *tag, dev READ8_MEMBER( z80ctc_device::read ) { - return m_channel[offset & 3].read(); + return m_channel[offset & 3]->read(); } @@ -107,7 +104,7 @@ READ8_MEMBER( z80ctc_device::read ) WRITE8_MEMBER( z80ctc_device::write ) { - m_channel[offset & 3].write(data); + m_channel[offset & 3]->write(data); } @@ -116,31 +113,51 @@ WRITE8_MEMBER( z80ctc_device::write ) // trigger //------------------------------------------------- -WRITE_LINE_MEMBER( z80ctc_device::trg0 ) { m_channel[0].trigger(state); } -WRITE_LINE_MEMBER( z80ctc_device::trg1 ) { m_channel[1].trigger(state); } -WRITE_LINE_MEMBER( z80ctc_device::trg2 ) { m_channel[2].trigger(state); } -WRITE_LINE_MEMBER( z80ctc_device::trg3 ) { m_channel[3].trigger(state); } +WRITE_LINE_MEMBER( z80ctc_device::trg0 ) { m_channel[0]->trigger(state != 0); } +WRITE_LINE_MEMBER( z80ctc_device::trg1 ) { m_channel[1]->trigger(state != 0); } +WRITE_LINE_MEMBER( z80ctc_device::trg2 ) { m_channel[2]->trigger(state != 0); } +WRITE_LINE_MEMBER( z80ctc_device::trg3 ) { m_channel[3]->trigger(state != 0); } //------------------------------------------------- -// device_start - device-specific startup +// device_add_mconfig - add device-specific +// machine configuration //------------------------------------------------- -void z80ctc_device::device_start() +MACHINE_CONFIG_START(z80ctc_device::device_add_mconfig) + MCFG_DEVICE_ADD("ch0", Z80CTC_CHANNEL, 0) + MCFG_DEVICE_ADD("ch1", Z80CTC_CHANNEL, 0) + MCFG_DEVICE_ADD("ch2", Z80CTC_CHANNEL, 0) + MCFG_DEVICE_ADD("ch3", Z80CTC_CHANNEL, 0) +MACHINE_CONFIG_END + + +//------------------------------------------------- +// device_resolve_objects - resolve objects that +// may be needed for other devices to set +// initial conditions at start time +//------------------------------------------------- + +void z80ctc_device::device_resolve_objects() { // resolve callbacks m_intr_cb.resolve_safe(); - m_zc0_cb.resolve_safe(); - m_zc1_cb.resolve_safe(); - m_zc2_cb.resolve_safe(); - m_zc3_cb.resolve_safe(); + for (int ch = 0; ch < 4; ch++) + { + m_zc_cb[ch].resolve_safe(); - // start each channel - m_channel[0].start(this, 0); - m_channel[1].start(this, 1); - m_channel[2].start(this, 2); - m_channel[3].start(this, 3); + // assign channel index + m_channel[ch]->m_index = ch; + } +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void z80ctc_device::device_start() +{ // register for save states save_item(NAME(m_vector)); } @@ -150,17 +167,11 @@ void z80ctc_device::device_start() // device_reset - device-specific reset //------------------------------------------------- -void z80ctc_device::device_reset() +void z80ctc_device::device_reset_after_children() { - // reset each channel - m_channel[0].reset(); - m_channel[1].reset(); - m_channel[2].reset(); - m_channel[3].reset(); - // check for interrupts interrupt_check(); - VPRINTF(("CTC Reset\n")); + LOG("CTC Reset\n"); } @@ -176,19 +187,19 @@ void z80ctc_device::device_reset() int z80ctc_device::z80daisy_irq_state() { - VPRINTF(("CTC IRQ state = %d%d%d%d\n", m_channel[0].m_int_state, m_channel[1].m_int_state, m_channel[2].m_int_state, m_channel[3].m_int_state)); + LOG("CTC IRQ state = %d%d%d%d\n", m_channel[0]->m_int_state, m_channel[1]->m_int_state, m_channel[2]->m_int_state, m_channel[3]->m_int_state); // loop over all channels int state = 0; - for (auto & channel : m_channel) + for (int ch = 0; ch < 4; ch++) { // if we're servicing a request, don't indicate more interrupts - if (channel.m_int_state & Z80_DAISY_IEO) + if (m_channel[ch]->m_int_state & Z80_DAISY_IEO) { state |= Z80_DAISY_IEO; break; } - state |= channel.m_int_state; + state |= m_channel[ch]->m_int_state; } return state; @@ -205,12 +216,12 @@ int z80ctc_device::z80daisy_irq_ack() // loop over all channels for (int ch = 0; ch < 4; ch++) { - ctc_channel &channel = m_channel[ch]; + z80ctc_channel_device &channel = *m_channel[ch]; // find the first channel with an interrupt requested if (channel.m_int_state & Z80_DAISY_INT) { - VPRINTF(("CTC IRQAck ch%d\n", ch)); + LOG("CTC IRQAck ch%d\n", ch); // clear interrupt, switch to the IEO state, and update the IRQs channel.m_int_state = Z80_DAISY_IEO; @@ -234,12 +245,12 @@ void z80ctc_device::z80daisy_irq_reti() // loop over all channels for (int ch = 0; ch < 4; ch++) { - ctc_channel &channel = m_channel[ch]; + z80ctc_channel_device &channel = *m_channel[ch]; // find the first channel with an IEO pending if (channel.m_int_state & Z80_DAISY_IEO) { - VPRINTF(("CTC IRQReti ch%d\n", ch)); + LOG("CTC IRQReti ch%d\n", ch); // clear the IEO state and update the IRQs channel.m_int_state &= ~Z80_DAISY_IEO; @@ -275,11 +286,12 @@ void z80ctc_device::interrupt_check() //************************************************************************** //------------------------------------------------- -// ctc_channel - constructor +// z80ctc_channel_device - constructor //------------------------------------------------- -z80ctc_device::ctc_channel::ctc_channel() - : m_device(nullptr), +z80ctc_channel_device::z80ctc_channel_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : device_t(mconfig, Z80CTC_CHANNEL, tag, owner, clock), + m_device(*this, DEVICE_SELF_OWNER), m_index(0), m_mode(0), m_tconst(0), @@ -292,30 +304,28 @@ z80ctc_device::ctc_channel::ctc_channel() //------------------------------------------------- -// start - set up at device start time +// device_start - set up at device start time //------------------------------------------------- -void z80ctc_device::ctc_channel::start(z80ctc_device *device, int index) +void z80ctc_channel_device::device_start() { // initialize state - m_device = device; - m_index = index; - m_timer = m_device->machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(z80ctc_device::ctc_channel::timer_callback), this)); + m_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(z80ctc_channel_device::timer_callback), this)); // register for save states - m_device->save_item(NAME(m_mode), m_index); - m_device->save_item(NAME(m_tconst), m_index); - m_device->save_item(NAME(m_down), m_index); - m_device->save_item(NAME(m_extclk), m_index); - m_device->save_item(NAME(m_int_state), m_index); + save_item(NAME(m_mode)); + save_item(NAME(m_tconst)); + save_item(NAME(m_down)); + save_item(NAME(m_extclk)); + save_item(NAME(m_int_state)); } //------------------------------------------------- -// reset - reset the channel +// device_reset - reset the channel //------------------------------------------------- -void z80ctc_device::ctc_channel::reset() +void z80ctc_channel_device::device_reset() { m_mode = RESET_ACTIVE; m_tconst = 0x100; @@ -328,7 +338,7 @@ void z80ctc_device::ctc_channel::reset() // period - return the current channel's period //------------------------------------------------- -attotime z80ctc_device::ctc_channel::period() const +attotime z80ctc_channel_device::period() const { // if reset active, no period if ((m_mode & RESET) == RESET_ACTIVE) @@ -337,7 +347,7 @@ attotime z80ctc_device::ctc_channel::period() const // if counter mode, no real period if ((m_mode & MODE) == MODE_COUNTER) { - m_device->logerror("CTC %d is CounterMode : Can't calculate period\n", m_index); + logerror("CounterMode : Can't calculate period\n"); return attotime::zero; } @@ -351,7 +361,7 @@ attotime z80ctc_device::ctc_channel::period() const // read - read the channel's state //------------------------------------------------- -uint8_t z80ctc_device::ctc_channel::read() +u8 z80ctc_channel_device::read() { // if we're in counter mode, just return the count if ((m_mode & MODE) == MODE_COUNTER || (m_mode & WAITING_FOR_TRIG)) @@ -362,7 +372,7 @@ uint8_t z80ctc_device::ctc_channel::read() { attotime period = m_device->clocks_to_attotime((m_mode & PRESCALER) == PRESCALER_16 ? 16 : 256); - VPRINTF_CHANNEL(("CTC clock %f\n",ATTOSECONDS_TO_HZ(period.attoseconds()))); + LOG("CTC clock %f\n",ATTOSECONDS_TO_HZ(period.attoseconds())); if (m_timer != nullptr) return ((int)(m_timer->remaining().as_double() / period.as_double()) + 1) & 0xff; @@ -376,12 +386,12 @@ uint8_t z80ctc_device::ctc_channel::read() // write - handle writes to a channel //------------------------------------------------- -void z80ctc_device::ctc_channel::write(uint8_t data) +void z80ctc_channel_device::write(u8 data) { // if we're waiting for a time constant, this is it if ((m_mode & CONSTANT) == CONSTANT_LOAD) { - VPRINTF_CHANNEL(("CTC ch.%d constant = %02x\n", m_index, data)); + LOG("Time constant = %02x\n", data); // set the time constant (0 -> 0x100) m_tconst = data ? data : 0x100; @@ -399,7 +409,7 @@ void z80ctc_device::ctc_channel::write(uint8_t data) if ((m_mode & TRIGGER) == TRIGGER_AUTO) { attotime curperiod = period(); - m_timer->adjust(curperiod, m_index, curperiod); + m_timer->adjust(curperiod, 0, curperiod); } // else set the bit indicating that we're waiting for the appropriate trigger @@ -421,7 +431,7 @@ void z80ctc_device::ctc_channel::write(uint8_t data) #endif { m_device->m_vector = data & 0xf8; - VPRINTF_CHANNEL(("CTC Vector = %02x\n", m_device->m_vector)); + LOG("Vector = %02x\n", m_device->m_vector); } // this must be a control word @@ -435,7 +445,7 @@ void z80ctc_device::ctc_channel::write(uint8_t data) // set the new mode m_mode = data; - VPRINTF_CHANNEL(("CTC ch.%d mode = %02x\n", m_index, data)); + LOG("Channel mode = %02x\n", data); // if we're being reset, clear out any pending timers for this channel if ((data & RESET) == RESET_ACTIVE) @@ -452,25 +462,22 @@ void z80ctc_device::ctc_channel::write(uint8_t data) // side-effects //------------------------------------------------- -void z80ctc_device::ctc_channel::trigger(uint8_t data) +void z80ctc_channel_device::trigger(bool state) { - // normalize data - data = data ? 1 : 0; - // see if the trigger value has changed - if (data != m_extclk) + if (state != m_extclk) { - m_extclk = data; + m_extclk = state; // see if this is the active edge of the trigger - if (((m_mode & EDGE) == EDGE_RISING && data) || ((m_mode & EDGE) == EDGE_FALLING && !data)) + if (((m_mode & EDGE) == EDGE_RISING && state) || ((m_mode & EDGE) == EDGE_FALLING && !state)) { // if we're waiting for a trigger, start the timer if ((m_mode & WAITING_FOR_TRIG) && (m_mode & MODE) == MODE_TIMER) { attotime curperiod = period(); - VPRINTF_CHANNEL(("CTC period %s\n", curperiod.as_string())); - m_timer->adjust(curperiod, m_index, curperiod); + LOG("Period = %s\n", curperiod.as_string()); + m_timer->adjust(curperiod, 0, curperiod); } // we're no longer waiting @@ -493,36 +500,19 @@ void z80ctc_device::ctc_channel::trigger(uint8_t data) // side-effects //------------------------------------------------- -TIMER_CALLBACK_MEMBER(z80ctc_device::ctc_channel::timer_callback) +TIMER_CALLBACK_MEMBER(z80ctc_channel_device::timer_callback) { // down counter has reached zero - see if we should interrupt if ((m_mode & INTERRUPT) == INTERRUPT_ON) { m_int_state |= Z80_DAISY_INT; - VPRINTF_CHANNEL(("CTC timer ch%d\n", m_index)); + LOG("Timer interrupt\n"); m_device->interrupt_check(); } // generate the clock pulse - switch (m_index) - { - case 0: - m_device->m_zc0_cb(1); - m_device->m_zc0_cb(0); - break; - case 1: - m_device->m_zc1_cb(1); - m_device->m_zc1_cb(0); - break; - case 2: - m_device->m_zc2_cb(1); - m_device->m_zc2_cb(0); - break; - case 3: - m_device->m_zc3_cb(1); - m_device->m_zc3_cb(0); - break; - } + m_device->m_zc_cb[m_index](1); + m_device->m_zc_cb[m_index](0); // reset the down counter m_down = m_tconst; diff --git a/src/devices/machine/z80ctc.h b/src/devices/machine/z80ctc.h index 7da3f480bf7..724a6dbb9c0 100644 --- a/src/devices/machine/z80ctc.h +++ b/src/devices/machine/z80ctc.h @@ -39,33 +39,68 @@ devcb = &downcast(*device).set_intr_callback(DEVCB_##_devcb); #define MCFG_Z80CTC_ZC0_CB(_devcb) \ - devcb = &downcast(*device).set_zc0_callback(DEVCB_##_devcb); + devcb = &downcast(*device).set_zc_callback<0>(DEVCB_##_devcb); #define MCFG_Z80CTC_ZC1_CB(_devcb) \ - devcb = &downcast(*device).set_zc1_callback(DEVCB_##_devcb); + devcb = &downcast(*device).set_zc_callback<1>(DEVCB_##_devcb); #define MCFG_Z80CTC_ZC2_CB(_devcb) \ - devcb = &downcast(*device).set_zc2_callback(DEVCB_##_devcb); + devcb = &downcast(*device).set_zc_callback<2>(DEVCB_##_devcb); //************************************************************************** // TYPE DEFINITIONS //************************************************************************** +// forward declaration +class z80ctc_device; + +// ======================> z80ctc_channel_device + +// a single channel within the CTC +class z80ctc_channel_device : public device_t +{ + friend class z80ctc_device; + +public: + // construction/destruction + z80ctc_channel_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + +protected: + // device-level overrides + virtual void device_start() override; + virtual void device_reset() override; + + u8 read(); + void write(u8 data); + + attotime period() const; + void trigger(bool state); + TIMER_CALLBACK_MEMBER(timer_callback); + + required_device m_device; // pointer back to our device + int m_index; // our channel index + u16 m_mode; // current mode + u16 m_tconst; // time constant + u16 m_down; // down counter (clock mode only) + bool m_extclk; // current signal from the external clock + emu_timer * m_timer; // array of active timers + u8 m_int_state; // interrupt status (for daisy chain) +}; // ======================> z80ctc_device class z80ctc_device : public device_t, public device_z80daisy_interface { + friend class z80ctc_channel_device; + public: // construction/destruction - z80ctc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + z80ctc_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); template devcb_base &set_intr_callback(Object &&cb) { return m_intr_cb.set_callback(std::forward(cb)); } - template devcb_base &set_zc0_callback(Object &&cb) { return m_zc0_cb.set_callback(std::forward(cb)); } - template devcb_base &set_zc1_callback(Object &&cb) { return m_zc1_cb.set_callback(std::forward(cb)); } - template devcb_base &set_zc2_callback(Object &&cb) { return m_zc2_cb.set_callback(std::forward(cb)); } + template devcb_base &set_zc_callback(Object &&cb) { return m_zc_cb[Channel].set_callback(std::forward(cb)); } // read/write handlers DECLARE_READ8_MEMBER( read ); @@ -75,12 +110,14 @@ public: DECLARE_WRITE_LINE_MEMBER( trg2 ); DECLARE_WRITE_LINE_MEMBER( trg3 ); - uint16_t get_channel_constant(uint8_t channel) { return m_channel[channel].m_tconst; } + u16 get_channel_constant(u8 channel) const { return m_channel[channel]->m_tconst; } protected: // device-level overrides + virtual void device_add_mconfig(machine_config &config) override; + virtual void device_resolve_objects() override; virtual void device_start() override; - virtual void device_reset() override; + virtual void device_reset_after_children() override; // z80daisy_interface overrides virtual int z80daisy_irq_state() override; @@ -91,46 +128,19 @@ private: // internal helpers void interrupt_check(); - // a single channel within the CTC - class ctc_channel - { - public: - ctc_channel(); - - void start(z80ctc_device *device, int index); - void reset(); - - uint8_t read(); - void write(uint8_t data); - - attotime period() const; - void trigger(uint8_t data); - TIMER_CALLBACK_MEMBER(timer_callback); - - z80ctc_device * m_device; // pointer back to our device - int m_index; // our channel index - uint16_t m_mode; // current mode - uint16_t m_tconst; // time constant - uint16_t m_down; // down counter (clock mode only) - uint8_t m_extclk; // current signal from the external clock - emu_timer * m_timer; // array of active timers - uint8_t m_int_state; // interrupt status (for daisy chain) - }; - // internal state devcb_write_line m_intr_cb; // interrupt callback - devcb_write_line m_zc0_cb; // channel 0 zero crossing callbacks - devcb_write_line m_zc1_cb; // channel 1 zero crossing callbacks - devcb_write_line m_zc2_cb; // channel 2 zero crossing callbacks - devcb_write_line m_zc3_cb; // channel 3 zero crossing callbacks = nullptr ? + devcb_write_line m_zc_cb[4]; // zero crossing/timer output callbacks - uint8_t m_vector; // interrupt vector - ctc_channel m_channel[4]; // data for each channel + u8 m_vector; // interrupt vector + + // subdevice for each channel + required_device_array m_channel; }; -// device type definition +// device type definitions DECLARE_DEVICE_TYPE(Z80CTC, z80ctc_device) - +DECLARE_DEVICE_TYPE(Z80CTC_CHANNEL, z80ctc_channel_device) #endif // MAME_MACHINE_Z80CTC_H -- cgit v1.2.3