diff options
Diffstat (limited to 'src/emu/machine/z80ctc.c')
-rw-r--r-- | src/emu/machine/z80ctc.c | 781 |
1 files changed, 401 insertions, 380 deletions
diff --git a/src/emu/machine/z80ctc.c b/src/emu/machine/z80ctc.c index da57ae6fb2f..dcaeee1e7e2 100644 --- a/src/emu/machine/z80ctc.c +++ b/src/emu/machine/z80ctc.c @@ -16,9 +16,9 @@ -/*************************************************************************** - DEBUGGING -***************************************************************************/ +//************************************************************************** +// DEBUGGING +//************************************************************************** #define VERBOSE 0 @@ -26,515 +26,536 @@ -/*************************************************************************** - CONSTANTS -***************************************************************************/ +//************************************************************************** +// CONSTANTS +//************************************************************************** -/* these are the bits of the incoming commands to the CTC */ -#define INTERRUPT 0x80 -#define INTERRUPT_ON 0x80 -#define INTERRUPT_OFF 0x00 +// 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; -#define MODE 0x40 -#define MODE_TIMER 0x00 -#define MODE_COUNTER 0x40 +const int MODE = 0x40; +const int MODE_TIMER = 0x00; +const int MODE_COUNTER = 0x40; -#define PRESCALER 0x20 -#define PRESCALER_256 0x20 -#define PRESCALER_16 0x00 +const int PRESCALER = 0x20; +const int PRESCALER_256 = 0x20; +const int PRESCALER_16 = 0x00; -#define EDGE 0x10 -#define EDGE_FALLING 0x00 -#define EDGE_RISING 0x10 +const int EDGE = 0x10; +const int EDGE_FALLING = 0x00; +const int EDGE_RISING = 0x10; -#define TRIGGER 0x08 -#define TRIGGER_AUTO 0x00 -#define TRIGGER_CLOCK 0x08 +const int TRIGGER = 0x08; +const int TRIGGER_AUTO = 0x00; +const int TRIGGER_CLOCK = 0x08; -#define CONSTANT 0x04 -#define CONSTANT_LOAD 0x04 -#define CONSTANT_NONE 0x00 +const int CONSTANT = 0x04; +const int CONSTANT_LOAD = 0x04; +const int CONSTANT_NONE = 0x00; -#define RESET 0x02 -#define RESET_CONTINUE 0x00 -#define RESET_ACTIVE 0x02 +const int RESET = 0x02; +const int RESET_CONTINUE = 0x00; +const int RESET_ACTIVE = 0x02; -#define CONTROL 0x01 -#define CONTROL_VECTOR 0x00 -#define CONTROL_WORD 0x01 +const int CONTROL = 0x01; +const int CONTROL_VECTOR = 0x00; +const int CONTROL_WORD = 0x01; -/* these extra bits help us keep things accurate */ -#define WAITING_FOR_TRIG 0x100 +// these extra bits help us keep things accurate +const int WAITING_FOR_TRIG = 0x100; -/*************************************************************************** - TYPE DEFINITIONS -***************************************************************************/ +//************************************************************************** +// DEVICE CONFIGURATION +//************************************************************************** -typedef struct _ctc_channel ctc_channel; -struct _ctc_channel +//------------------------------------------------- +// z80ctc_device_config - constructor +//------------------------------------------------- + +z80ctc_device_config::z80ctc_device_config(const machine_config &mconfig, const char *tag, const device_config *owner, UINT32 clock) + : device_config(mconfig, static_alloc_device_config, tag, owner, clock), + device_config_z80daisy_interface(mconfig, *this) { - devcb_resolved_write_line zc; /* zero crossing callbacks */ +} - UINT8 notimer; /* no timer masks */ - UINT16 mode; /* current mode */ - UINT16 tconst; /* time constant */ - UINT16 down; /* down counter (clock mode only) */ - UINT8 extclk; /* current signal from the external clock */ - emu_timer * timer; /* array of active timers */ - UINT8 int_state; /* interrupt status (for daisy chain) */ -}; +//------------------------------------------------- +// static_alloc_device_config - allocate a new +// configuration object +//------------------------------------------------- -typedef struct _z80ctc z80ctc; -struct _z80ctc +device_config *z80ctc_device_config::static_alloc_device_config(const machine_config &mconfig, const char *tag, const device_config *owner, UINT32 clock) { - devcb_resolved_write_line intr; /* interrupt callback */ + return global_alloc(z80ctc_device_config(mconfig, tag, owner, clock)); +} - UINT8 vector; /* interrupt vector */ - attotime period16; /* 16/system clock */ - attotime period256; /* 256/system clock */ - ctc_channel channel[4]; /* data for each channel */ -}; +//------------------------------------------------- +// alloc_device - allocate a new device object +//------------------------------------------------- +device_t *z80ctc_device_config::alloc_device(running_machine &machine) const +{ + return auto_alloc(&machine, z80ctc_device(machine, *this)); +} -/*************************************************************************** - FUNCTION PROTOTYPES -***************************************************************************/ -static int z80ctc_irq_state(running_device *device); -static int z80ctc_irq_ack(running_device *device); -static void z80ctc_irq_reti(running_device *device); +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void z80ctc_device_config::device_config_complete() +{ + // inherit a copy of the static data + const z80ctc_interface *intf = reinterpret_cast<const z80ctc_interface *>(static_config()); + if (intf != NULL) + *static_cast<z80ctc_interface *>(this) = *intf; + + // or initialize to defaults if none provided + else + { + m_notimer = 0; + memset(&m_intr, 0, sizeof(m_intr)); + memset(&m_zc0, 0, sizeof(m_zc0)); + memset(&m_zc1, 0, sizeof(m_zc1)); + memset(&m_zc2, 0, sizeof(m_zc2)); + } +} + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** -/*************************************************************************** - INLINE FUNCTIONS -***************************************************************************/ +//------------------------------------------------- +// z80ctc_device - constructor +//------------------------------------------------- -INLINE z80ctc *get_safe_token(running_device *device) +z80ctc_device::z80ctc_device(running_machine &_machine, const z80ctc_device_config &_config) + : device_t(_machine, _config), + device_z80daisy_interface(_machine, _config, *this), + m_config(_config) { - assert(device != NULL); - assert(device->token != NULL); - assert(device->type == Z80CTC); - return (z80ctc *)device->token; } +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- -/*************************************************************************** - INTERNAL STATE MANAGEMENT -***************************************************************************/ - -static void interrupt_check(running_device *device) +void z80ctc_device::device_start() { - z80ctc *ctc = get_safe_token(device); - int state = (z80ctc_irq_state(device) & Z80_DAISY_INT) ? ASSERT_LINE : CLEAR_LINE; + m_period16 = attotime_mul(ATTOTIME_IN_HZ(m_clock), 16); + m_period256 = attotime_mul(ATTOTIME_IN_HZ(m_clock), 256); + + // resolve callbacks + devcb_resolve_write_line(&m_intr, &m_config.m_intr, this); + + // start each channel + m_channel[0].start(this, 0, (m_config.m_notimer & NOTIMER_0) != 0, &m_config.m_zc0); + m_channel[1].start(this, 1, (m_config.m_notimer & NOTIMER_1) != 0, &m_config.m_zc1); + m_channel[2].start(this, 2, (m_config.m_notimer & NOTIMER_2) != 0, &m_config.m_zc2); + m_channel[3].start(this, 3, (m_config.m_notimer & NOTIMER_3) != 0, NULL); + + // register for save states + state_save_register_device_item(this, 0, m_vector); +} + - devcb_call_write_line(&ctc->intr, state); +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void z80ctc_device::device_reset() +{ + // 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")); } -static TIMER_CALLBACK( timercallback ) + + +//************************************************************************** +// DAISY CHAIN INTERFACE +//************************************************************************** + +//------------------------------------------------- +// z80daisy_irq_state - return the overall IRQ +// state for this device +//------------------------------------------------- + +int z80ctc_device::z80daisy_irq_state() { - running_device *device = (running_device *)ptr; - z80ctc *ctc = get_safe_token(device); - ctc_channel *channel = &ctc->channel[param]; + 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)); - /* down counter has reached zero - see if we should interrupt */ - if ((channel->mode & INTERRUPT) == INTERRUPT_ON) + // loop over all channels + int state = 0; + for (int ch = 0; ch < 4; ch++) { - channel->int_state |= Z80_DAISY_INT; - VPRINTF(("CTC timer ch%d\n", param)); - interrupt_check(device); - } + ctc_channel &channel = m_channel[ch]; - /* generate the clock pulse */ - devcb_call_write_line(&channel->zc, 1); - devcb_call_write_line(&channel->zc, 0); + // if we're servicing a request, don't indicate more interrupts + if (channel.m_int_state & Z80_DAISY_IEO) + { + state |= Z80_DAISY_IEO; + break; + } + state |= channel.m_int_state; + } - /* reset the down counter */ - channel->down = channel->tconst; + return state; } +//------------------------------------------------- +// z80daisy_irq_ack - acknowledge an IRQ and +// return the appropriate vector +//------------------------------------------------- -/*************************************************************************** - INITIALIZATION/CONFIGURATION -***************************************************************************/ - -attotime z80ctc_getperiod(running_device *device, int ch) +int z80ctc_device::z80daisy_irq_ack() { - z80ctc *ctc = get_safe_token(device); - ctc_channel *channel = &ctc->channel[ch]; - attotime period; + // loop over all channels + for (int ch = 0; ch < 4; ch++) + { + ctc_channel &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)); + + // clear interrupt, switch to the IEO state, and update the IRQs + channel.m_int_state = Z80_DAISY_IEO; + interrupt_check(); + return m_vector + ch * 2; + } + } + + logerror("z80ctc_irq_ack: failed to find an interrupt to ack!\n"); + return m_vector; +} - /* if reset active, no period */ - if ((channel->mode & RESET) == RESET_ACTIVE) - return attotime_zero; - /* if counter mode, no real period */ - if ((channel->mode & MODE) == MODE_COUNTER) +//------------------------------------------------- +// z80daisy_irq_reti - clear the interrupt +// pending state to allow other interrupts through +//------------------------------------------------- + +void z80ctc_device::z80daisy_irq_reti() +{ + // loop over all channels + for (int ch = 0; ch < 4; ch++) { - logerror("CTC %d is CounterMode : Can't calculate period\n", ch ); - return attotime_zero; + ctc_channel &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)); + + // clear the IEO state and update the IRQs + channel.m_int_state &= ~Z80_DAISY_IEO; + interrupt_check(); + return; + } } - /* compute the period */ - period = ((channel->mode & PRESCALER) == PRESCALER_16) ? ctc->period16 : ctc->period256; - return attotime_mul(period, channel->tconst); + logerror("z80ctc_irq_reti: failed to find an interrupt to clear IEO on!\n"); } -/*************************************************************************** - WRITE HANDLERS -***************************************************************************/ +//************************************************************************** +// INTERNAL STATE MANAGEMENT +//************************************************************************** -WRITE8_DEVICE_HANDLER( z80ctc_w ) +//------------------------------------------------- +// interrupt_check - look for pending interrupts +// and update the line +//------------------------------------------------- + +void z80ctc_device::interrupt_check() { - z80ctc *ctc = get_safe_token(device); - int ch = offset & 3; - ctc_channel *channel = &ctc->channel[ch]; - int mode; + int state = (z80daisy_irq_state() & Z80_DAISY_INT) ? ASSERT_LINE : CLEAR_LINE; + devcb_call_write_line(&m_intr, state); +} - /* get the current mode */ - mode = channel->mode; - /* if we're waiting for a time constant, this is it */ - if ((mode & CONSTANT) == CONSTANT_LOAD) - { - VPRINTF(("CTC ch.%d constant = %02x\n", ch, data)); - /* set the time constant (0 -> 0x100) */ - channel->tconst = data ? data : 0x100; +//************************************************************************* +// CTC CHANNELS +//************************************************************************** - /* clear the internal mode -- we're no longer waiting */ - channel->mode &= ~CONSTANT; +//------------------------------------------------- +// ctc_channel - constructor +//------------------------------------------------- - /* also clear the reset, since the constant gets it going again */ - channel->mode &= ~RESET; +z80ctc_device::ctc_channel::ctc_channel() + : m_notimer(false), + m_mode(0), + m_tconst(0), + m_down(0), + m_extclk(0), + m_timer(NULL), + m_int_state(0) +{ + memset(&m_zc, 0, sizeof(m_zc)); +} - /* if we're in timer mode.... */ - if ((mode & MODE) == MODE_TIMER) - { - /* if we're triggering on the time constant, reset the down counter now */ - if ((mode & TRIGGER) == TRIGGER_AUTO) - { - if (!channel->notimer) - { - attotime period = ((mode & PRESCALER) == PRESCALER_16) ? ctc->period16 : ctc->period256; - period = attotime_mul(period, channel->tconst); - timer_adjust_periodic(channel->timer, period, ch, period); - } - else - timer_adjust_oneshot(channel->timer, attotime_never, 0); - } +//------------------------------------------------- +// start - set up at device start time +//------------------------------------------------- - /* else set the bit indicating that we're waiting for the appropriate trigger */ - else - channel->mode |= WAITING_FOR_TRIG; - } +void z80ctc_device::ctc_channel::start(z80ctc_device *device, int index, bool notimer, const devcb_write_line *write_line) +{ + // initialize state + m_device = device; + m_index = index; + if (write_line != NULL) + devcb_resolve_write_line(&m_zc, write_line, m_device); + m_notimer = notimer; + m_timer = timer_alloc(&m_device->m_machine, static_timer_callback, this); + + // register for save states + state_save_register_device_item(m_device, m_index, m_mode); + state_save_register_device_item(m_device, m_index, m_tconst); + state_save_register_device_item(m_device, m_index, m_down); + state_save_register_device_item(m_device, m_index, m_extclk); + state_save_register_device_item(m_device, m_index, m_int_state); +} - /* also set the down counter in case we're clocking externally */ - channel->down = channel->tconst; - /* all done here */ - return; - } +//------------------------------------------------- +// reset - reset the channel +//------------------------------------------------- - /* if we're writing the interrupt vector, handle it specially */ -#if 0 /* Tatsuyuki Satoh changes */ - /* The 'Z80family handbook' wrote, */ - /* interrupt vector is able to set for even channel (0 or 2) */ - if ((data & CONTROL) == CONTROL_VECTOR && (ch&1) == 0) -#else - if ((data & CONTROL) == CONTROL_VECTOR && ch == 0) -#endif - { - ctc->vector = data & 0xf8; - logerror("CTC Vector = %02x\n", ctc->vector); - return; - } +void z80ctc_device::ctc_channel::reset() +{ + m_mode = RESET_ACTIVE; + m_tconst = 0x100; + timer_adjust_oneshot(m_timer, attotime_never, 0); + m_int_state = 0; +} - /* this must be a control word */ - if ((data & CONTROL) == CONTROL_WORD) - { - /* set the new mode */ - channel->mode = data; - VPRINTF(("CTC ch.%d mode = %02x\n", ch, data)); - /* if we're being reset, clear out any pending timers for this channel */ - if ((data & RESET) == RESET_ACTIVE) - { - timer_adjust_oneshot(channel->timer, attotime_never, 0); - /* note that we don't clear the interrupt state here! */ - } +//------------------------------------------------- +// period - return the current channel's period +//------------------------------------------------- + +attotime z80ctc_device::ctc_channel::period() const +{ + // if reset active, no period + if ((m_mode & RESET) == RESET_ACTIVE) + return attotime_zero; - /* all done here */ - return; + // if counter mode, no real period + if ((m_mode & MODE) == MODE_COUNTER) + { + logerror("CTC %d is CounterMode : Can't calculate period\n", m_index); + return attotime_zero; } -} + // compute the period + attotime period = ((m_mode & PRESCALER) == PRESCALER_16) ? m_device->m_period16 : m_device->m_period256; + return attotime_mul(period, m_tconst); +} -/*************************************************************************** - READ HANDLERS -***************************************************************************/ +//------------------------------------------------- +// read - read the channel's state +//------------------------------------------------- -READ8_DEVICE_HANDLER( z80ctc_r ) +UINT8 z80ctc_device::ctc_channel::read() { - z80ctc *ctc = get_safe_token(device); - int ch = offset & 3; - ctc_channel *channel = &ctc->channel[ch]; - - /* if we're in counter mode, just return the count */ - if ((channel->mode & MODE) == MODE_COUNTER || (channel->mode & WAITING_FOR_TRIG)) - return channel->down; + // if we're in counter mode, just return the count + if ((m_mode & MODE) == MODE_COUNTER || (m_mode & WAITING_FOR_TRIG)) + return m_down; - /* else compute the down counter value */ + // else compute the down counter value else { - attotime period = ((channel->mode & PRESCALER) == PRESCALER_16) ? ctc->period16 : ctc->period256; + attotime period = ((m_mode & PRESCALER) == PRESCALER_16) ? m_device->m_period16 : m_device->m_period256; VPRINTF(("CTC clock %f\n",ATTOSECONDS_TO_HZ(period.attoseconds))); - if (channel->timer != NULL) - return ((int)(attotime_to_double(timer_timeleft(channel->timer)) / attotime_to_double(period)) + 1) & 0xff; + if (m_timer != NULL) + return ((int)(attotime_to_double(timer_timeleft(m_timer)) / attotime_to_double(period)) + 1) & 0xff; else return 0; } } +//------------------------------------------------- +// write - handle writes to a channel +//------------------------------------------------- -/*************************************************************************** - EXTERNAL TRIGGERS -***************************************************************************/ - -static void z80ctc_trg_w(running_device *device, int ch, UINT8 data) +void z80ctc_device::ctc_channel::write(UINT8 data) { - z80ctc *ctc = get_safe_token(device); - ctc_channel *channel = &ctc->channel[ch]; + // if we're waiting for a time constant, this is it + if ((m_mode & CONSTANT) == CONSTANT_LOAD) + { + VPRINTF(("CTC ch.%d constant = %02x\n", m_index, data)); - /* normalize data */ - data = data ? 1 : 0; + // set the time constant (0 -> 0x100) + m_tconst = data ? data : 0x100; - /* see if the trigger value has changed */ - if (data != channel->extclk) - { - channel->extclk = data; + // clear the internal mode -- we're no longer waiting + m_mode &= ~CONSTANT; - /* see if this is the active edge of the trigger */ - if (((channel->mode & EDGE) == EDGE_RISING && data) || ((channel->mode & EDGE) == EDGE_FALLING && !data)) + // also clear the reset, since the constant gets it going again + m_mode &= ~RESET; + + // if we're in timer mode.... + if ((m_mode & MODE) == MODE_TIMER) { - /* if we're waiting for a trigger, start the timer */ - if ((channel->mode & WAITING_FOR_TRIG) && (channel->mode & MODE) == MODE_TIMER) + // if we're triggering on the time constant, reset the down counter now + if ((m_mode & TRIGGER) == TRIGGER_AUTO) { - if (!channel->notimer) + if (!m_notimer) { - attotime period = ((channel->mode & PRESCALER) == PRESCALER_16) ? ctc->period16 : ctc->period256; - period = attotime_mul(period, channel->tconst); - - VPRINTF(("CTC period %s\n", attotime_string(period, 9))); - timer_adjust_periodic(channel->timer, period, ch, period); + attotime curperiod = period(); + timer_adjust_periodic(m_timer, curperiod, m_index, curperiod); } else - { - VPRINTF(("CTC disabled\n")); - - timer_adjust_oneshot(channel->timer, attotime_never, 0); - } + timer_adjust_oneshot(m_timer, attotime_never, 0); } - /* we're no longer waiting */ - channel->mode &= ~WAITING_FOR_TRIG; - - /* if we're clocking externally, decrement the count */ - if ((channel->mode & MODE) == MODE_COUNTER) - { - channel->down--; - - /* if we hit zero, do the same thing as for a timer interrupt */ - if (!channel->down) - { - void *ptr = (void *)device; - timercallback(device->machine, ptr, ch); - } - } + // else set the bit indicating that we're waiting for the appropriate trigger + else + m_mode |= WAITING_FOR_TRIG; } - } -} -WRITE_LINE_DEVICE_HANDLER( z80ctc_trg0_w ) { z80ctc_trg_w(device, 0, state); } -WRITE_LINE_DEVICE_HANDLER( z80ctc_trg1_w ) { z80ctc_trg_w(device, 1, state); } -WRITE_LINE_DEVICE_HANDLER( z80ctc_trg2_w ) { z80ctc_trg_w(device, 2, state); } -WRITE_LINE_DEVICE_HANDLER( z80ctc_trg3_w ) { z80ctc_trg_w(device, 3, state); } - - -/*************************************************************************** - DAISY CHAIN INTERFACE -***************************************************************************/ - -static int z80ctc_irq_state(running_device *device) -{ - z80ctc *ctc = get_safe_token(device); - int state = 0; - int ch; - - VPRINTF(("CTC IRQ state = %d%d%d%d\n", ctc->channel[0].int_state, ctc->channel[1].int_state, ctc->channel[2].int_state, ctc->channel[3].int_state)); + // also set the down counter in case we're clocking externally + m_down = m_tconst; + } - /* loop over all channels */ - for (ch = 0; ch < 4; ch++) + // if we're writing the interrupt vector, handle it specially +#if 0 /* Tatsuyuki Satoh changes */ + // The 'Z80family handbook' wrote, + // interrupt vector is able to set for even channel (0 or 2) + else if ((data & CONTROL) == CONTROL_VECTOR && (m_index & 1) == 0) +#else + else if ((data & CONTROL) == CONTROL_VECTOR && m_index == 0) +#endif { - ctc_channel *channel = &ctc->channel[ch]; - - /* if we're servicing a request, don't indicate more interrupts */ - if (channel->int_state & Z80_DAISY_IEO) - { - state |= Z80_DAISY_IEO; - break; - } - state |= channel->int_state; + m_device->m_vector = data & 0xf8; + logerror("CTC Vector = %02x\n", m_device->m_vector); } - return state; -} - - -static int z80ctc_irq_ack(running_device *device) -{ - z80ctc *ctc = get_safe_token(device); - int ch; - - /* loop over all channels */ - for (ch = 0; ch < 4; ch++) + // this must be a control word + else if ((data & CONTROL) == CONTROL_WORD) { - ctc_channel *channel = &ctc->channel[ch]; + // set the new mode + m_mode = data; + VPRINTF(("CTC ch.%d mode = %02x\n", m_index, data)); - /* find the first channel with an interrupt requested */ - if (channel->int_state & Z80_DAISY_INT) + // if we're being reset, clear out any pending timers for this channel + if ((data & RESET) == RESET_ACTIVE) { - VPRINTF(("CTC IRQAck ch%d\n", ch)); - - /* clear interrupt, switch to the IEO state, and update the IRQs */ - channel->int_state = Z80_DAISY_IEO; - interrupt_check(device); - return ctc->vector + ch * 2; + timer_adjust_oneshot(m_timer, attotime_never, 0); + // note that we don't clear the interrupt state here! } } - - logerror("z80ctc_irq_ack: failed to find an interrupt to ack!\n"); - return ctc->vector; } -static void z80ctc_irq_reti(running_device *device) +//------------------------------------------------- +// trigger - clock this channel and handle any +// side-effects +//------------------------------------------------- + +void z80ctc_device::ctc_channel::trigger(UINT8 data) { - z80ctc *ctc = get_safe_token(device); - int ch; + // normalize data + data = data ? 1 : 0; - /* loop over all channels */ - for (ch = 0; ch < 4; ch++) + // see if the trigger value has changed + if (data != m_extclk) { - ctc_channel *channel = &ctc->channel[ch]; + m_extclk = data; - /* find the first channel with an IEO pending */ - if (channel->int_state & Z80_DAISY_IEO) + // see if this is the active edge of the trigger + if (((m_mode & EDGE) == EDGE_RISING && data) || ((m_mode & EDGE) == EDGE_FALLING && !data)) { - VPRINTF(("CTC IRQReti ch%d\n", ch)); + // if we're waiting for a trigger, start the timer + if ((m_mode & WAITING_FOR_TRIG) && (m_mode & MODE) == MODE_TIMER) + { + if (!m_notimer) + { + attotime curperiod = period(); + VPRINTF(("CTC period %s\n", attotime_string(curperiod, 9))); + timer_adjust_periodic(m_timer, curperiod, m_index, curperiod); + } + else + { + VPRINTF(("CTC disabled\n")); + timer_adjust_oneshot(m_timer, attotime_never, 0); + } + } - /* clear the IEO state and update the IRQs */ - channel->int_state &= ~Z80_DAISY_IEO; - interrupt_check(device); - return; + // we're no longer waiting + m_mode &= ~WAITING_FOR_TRIG; + + // if we're clocking externally, decrement the count + if ((m_mode & MODE) == MODE_COUNTER) + { + // if we hit zero, do the same thing as for a timer interrupt + if (--m_down == 0) + timer_callback(); + } } } - - logerror("z80ctc_irq_reti: failed to find an interrupt to clear IEO on!\n"); } -static DEVICE_START( z80ctc ) + +//------------------------------------------------- +// trigger - clock this channel and handle any +// side-effects +//------------------------------------------------- + +void z80ctc_device::ctc_channel::timer_callback() { - const z80ctc_interface *intf = (const z80ctc_interface *)device->baseconfig().static_config; - z80ctc *ctc = get_safe_token(device); - astring tempstring; - int ch; - - ctc->period16 = attotime_mul(ATTOTIME_IN_HZ(device->clock), 16); - ctc->period256 = attotime_mul(ATTOTIME_IN_HZ(device->clock), 256); - for (ch = 0; ch < 4; ch++) + // down counter has reached zero - see if we should interrupt + if ((m_mode & INTERRUPT) == INTERRUPT_ON) { - ctc_channel *channel = &ctc->channel[ch]; - void *ptr = (void *)device; - channel->notimer = (intf->notimer >> ch) & 1; - channel->timer = timer_alloc(device->machine, timercallback, ptr); + m_int_state |= Z80_DAISY_INT; + VPRINTF(("CTC timer ch%d\n", m_index)); + m_device->interrupt_check(); } - /* resolve callbacks */ - devcb_resolve_write_line(&ctc->intr, &intf->intr, device); - devcb_resolve_write_line(&ctc->channel[0].zc, &intf->zc0, device); - devcb_resolve_write_line(&ctc->channel[1].zc, &intf->zc1, device); - devcb_resolve_write_line(&ctc->channel[2].zc, &intf->zc2, device); - - /* register for save states */ - state_save_register_device_item(device, 0, ctc->vector); - for (ch = 0; ch < 4; ch++) - { - ctc_channel *channel = &ctc->channel[ch]; - state_save_register_device_item(device, ch, channel->mode); - state_save_register_device_item(device, ch, channel->tconst); - state_save_register_device_item(device, ch, channel->down); - state_save_register_device_item(device, ch, channel->extclk); - state_save_register_device_item(device, ch, channel->int_state); - } + // generate the clock pulse + devcb_call_write_line(&m_zc, 1); + devcb_call_write_line(&m_zc, 0); + + // reset the down counter + m_down = m_tconst; } -static DEVICE_RESET( z80ctc ) -{ - z80ctc *ctc = get_safe_token(device); - int ch; - /* set up defaults */ - for (ch = 0; ch < 4; ch++) - { - ctc_channel *channel = &ctc->channel[ch]; - channel->mode = RESET_ACTIVE; - channel->tconst = 0x100; - timer_adjust_oneshot(channel->timer, attotime_never, 0); - channel->int_state = 0; - } - interrupt_check(device); - VPRINTF(("CTC Reset\n")); -} +//************************************************************************** +// GLOBAL STUBS +//************************************************************************** +WRITE8_DEVICE_HANDLER( z80ctc_w ) { downcast<z80ctc_device *>(device)->write(offset & 3, data); } +READ8_DEVICE_HANDLER( z80ctc_r ) { return downcast<z80ctc_device *>(device)->read(offset & 3); } -DEVICE_GET_INFO( z80ctc ) -{ - switch (state) - { - /* --- the following bits of info are returned as 64-bit signed integers --- */ - case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(z80ctc); break; - case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break; - case DEVINFO_INT_CLASS: info->i = DEVICE_CLASS_PERIPHERAL; break; - - /* --- the following bits of info are returned as pointers to data or functions --- */ - case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(z80ctc);break; - case DEVINFO_FCT_STOP: /* Nothing */ break; - case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(z80ctc);break; - case DEVINFO_FCT_IRQ_STATE: info->f = (genf *)z80ctc_irq_state; break; - case DEVINFO_FCT_IRQ_ACK: info->f = (genf *)z80ctc_irq_ack; break; - case DEVINFO_FCT_IRQ_RETI: info->f = (genf *)z80ctc_irq_reti; break; - - /* --- the following bits of info are returned as NULL-terminated strings --- */ - case DEVINFO_STR_NAME: strcpy(info->s, "Zilog Z80 CTC"); break; - case DEVINFO_STR_FAMILY: strcpy(info->s, "Z80"); break; - case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; - case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; - case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; - } -} - +WRITE_LINE_DEVICE_HANDLER( z80ctc_trg0_w ) { downcast<z80ctc_device *>(device)->trigger(0, state); } +WRITE_LINE_DEVICE_HANDLER( z80ctc_trg1_w ) { downcast<z80ctc_device *>(device)->trigger(1, state); } +WRITE_LINE_DEVICE_HANDLER( z80ctc_trg2_w ) { downcast<z80ctc_device *>(device)->trigger(2, state); } +WRITE_LINE_DEVICE_HANDLER( z80ctc_trg3_w ) { downcast<z80ctc_device *>(device)->trigger(3, state); } |