diff options
Diffstat (limited to 'src/devices/machine/scnxx562.cpp')
-rw-r--r-- | src/devices/machine/scnxx562.cpp | 240 |
1 files changed, 123 insertions, 117 deletions
diff --git a/src/devices/machine/scnxx562.cpp b/src/devices/machine/scnxx562.cpp index a14110879d4..e49c586d00c 100644 --- a/src/devices/machine/scnxx562.cpp +++ b/src/devices/machine/scnxx562.cpp @@ -1,4 +1,4 @@ -// license:BSD-3-Clause +// license:BSD-3-Clause // copyright-holders: Joakim Larsson Edstrom /*************************************************************************** @@ -87,7 +87,6 @@ DONE (x) (p=partly) NMOS CMOS /* Useful temporary debug printout format */ // printf("TAG %s%s Data:%d\n", __PRETTY_FUNCTION__, owner()->tag(), data); -#define LOG_GENERAL (1U << 0) #define LOG_R (1U << 1) #define LOG_TX (1U << 2) #define LOG_RX (1U << 3) @@ -207,23 +206,6 @@ void duscc_device::device_start() { LOG("%s\n", FUNCNAME); - // resolve callbacks - m_out_txda_cb.resolve_safe(); - m_out_dtra_cb.resolve_safe(); - m_out_rtsa_cb.resolve_safe(); - m_out_synca_cb.resolve_safe(); - m_out_rtxca_cb.resolve_safe(); - m_out_trxca_cb.resolve_safe(); - - m_out_txdb_cb.resolve_safe(); - m_out_dtrb_cb.resolve_safe(); - m_out_rtsb_cb.resolve_safe(); - m_out_syncb_cb.resolve_safe(); - m_out_rtxcb_cb.resolve_safe(); - m_out_trxcb_cb.resolve_safe(); - - m_out_int_cb.resolve_safe(); - // state saving - stuff with runtime values save_item(NAME(m_int_state)); save_item(NAME(m_gsr)); @@ -242,7 +224,7 @@ void duscc_device::device_start() void duscc_device::device_reset() { - LOG("%s %s \n",tag(), FUNCNAME); + LOG("%s\n", FUNCNAME); m_chanA->reset(); m_chanB->reset(); @@ -297,7 +279,7 @@ int duscc_device::z80daisy_irq_state() { int state = 0; - LOGINT("%s %s A:[%02x][%02x][%02x][%02x] B:[%02x][%02x][%02x][%02x] ",tag(), FUNCNAME, + LOGINT("%s A:[%02x][%02x][%02x][%02x] B:[%02x][%02x][%02x][%02x] ", FUNCNAME, m_int_state[0], m_int_state[1], m_int_state[2], m_int_state[3], m_int_state[4], m_int_state[5], m_int_state[6], m_int_state[7]); @@ -325,7 +307,7 @@ int duscc_device::z80daisy_irq_state() int duscc_device::z80daisy_irq_ack() { - LOGINT("%s %s()\n",tag(), FUNCNAME); + LOGINT("%s()\n", FUNCNAME); // loop over all interrupt sources for (auto & elem : m_int_state) @@ -360,7 +342,7 @@ int duscc_device::z80daisy_irq_ack() void duscc_device::z80daisy_irq_reti() { - LOGINT("%s %s \n",tag(), FUNCNAME); + LOGINT("%s\n", FUNCNAME); // loop over all interrupt sources for (auto & elem : m_int_state) @@ -379,7 +361,7 @@ void duscc_device::z80daisy_irq_reti() uint8_t duscc_device::iack() { - LOGINT("%s %s - returning vector:%02x\n",tag(), FUNCNAME, m_ivrm); + LOGINT("%s - returning vector:%02x\n", FUNCNAME, m_ivrm); int vec = z80daisy_irq_ack(); z80daisy_irq_reti(); return vec; @@ -387,8 +369,8 @@ uint8_t duscc_device::iack() void duscc_device::check_interrupts() { - LOGINT("%s %s()\n",tag(), FUNCNAME); int state = (z80daisy_irq_state() & Z80_DAISY_INT) ? ASSERT_LINE : CLEAR_LINE; + LOGINT("%s(icr %02x ierA %02x ierB %02x state %d)\n", FUNCNAME, m_icr, m_chanA->m_ier, m_chanB->m_ier, state); // "If no interrupt is pending, an H'FF' is output when reading the IVRM." if (state == CLEAR_LINE) @@ -406,7 +388,7 @@ void duscc_device::check_interrupts() void duscc_device::reset_interrupts() { - LOGINT("%s %s\n",tag(), FUNCNAME); + LOGINT("%s\n", FUNCNAME); // reset internal interrupt sources for (auto & elem : m_int_state) @@ -435,7 +417,7 @@ uint8_t duscc_device::modify_vector(uint8_t vec, int index, uint8_t src) 1 1 1 Ch B external or C/T status -------------------------------------------------- */ - LOGINT("%s %c %s, vec:%02x src=%02x\n",tag(), 'A' + index, FUNCNAME, vec, src); + LOGINT("%c %s, vec:%02x src=%02x\n", 'A' + index, FUNCNAME, vec, src); // TODO: Prevent modification if no vector has been programmed, even if it is the default vector. if ((m_icr & REG_ICR_VEC_MOD) != 0) // Affect vector? @@ -472,6 +454,35 @@ uint8_t duscc_device::modify_vector(uint8_t vec, int index, uint8_t src) 7. General status register (GSR) */ +int duscc_device::interrupt_priority(int index, int state) +{ + int priority_level = 0; + + switch (m_icr & REG_ICR_PRIO_MASK) + { + case REG_ICR_PRIO_AHI: priority_level = state + (index == CHANNEL_A ? 0 : 4); break; + case REG_ICR_PRIO_BHI: priority_level = state + (index == CHANNEL_A ? 4 : 0); break; + case REG_ICR_PRIO_AINT: priority_level = state * 2 + (index == CHANNEL_A ? 0 : 1); break; + case REG_ICR_PRIO_BINT: priority_level = state * 2 + (index == CHANNEL_A ? 1 : 0); break; + default: logerror("DUSCC Programming error, please report/fix\n"); // Will not happen + } + + return priority_level; +} + +void duscc_device::clear_interrupt(int index, int state) +{ + LOGINT("%s:%c %02x\n", FUNCNAME, 'A' + index, state); + + m_int_state[interrupt_priority(index, state)] &= ~Z80_DAISY_INT; + if ((m_icr & (index == CHANNEL_A ? REG_ICR_CHA : REG_ICR_CHB)) == 0) + { + LOGINT("The Interrupt Control Register [%02x] bit for this channel is not set, blocking attempt to interrupt\n", m_icr); + return; + } + check_interrupts(); +} + //----------------------------------------------------------------------- // trigger_interrupt - called when a potential interrupt condition occurs and will only issue an interrupt if the DUSCC is // programmed to do so. @@ -482,7 +493,7 @@ void duscc_device::trigger_interrupt(int index, int state) uint8_t source = 0; int priority_level = 0; - LOGINT("%s %s:%c %02x \n",FUNCNAME, tag(), 'A' + index, state); + LOGINT("%s:%c %02x \n",FUNCNAME, 'A' + index, state); // The Interrupt Control Register (ICR) bits, must be set for the corresponding channel // ICR Check is probably by the caller but we check again to be sure @@ -493,14 +504,7 @@ void duscc_device::trigger_interrupt(int index, int state) } // Modify priority level - switch (m_icr & REG_ICR_PRIO_MASK) - { - case REG_ICR_PRIO_AHI: priority_level = state + (index == CHANNEL_A ? 0 : 4); break; - case REG_ICR_PRIO_BHI: priority_level = state + (index == CHANNEL_A ? 4 : 0); break; - case REG_ICR_PRIO_AINT: priority_level = state * 2 + (index == CHANNEL_A ? 0 : 1); break; - case REG_ICR_PRIO_BINT: priority_level = state * 2 + (index == CHANNEL_A ? 1 : 0); break; - default: logerror("DUSCC Programming error, please report/fix\n"); // Will not happen - } + priority_level = interrupt_priority(index, state); // Vector modification requested? source = state + (index == CHANNEL_A ? 0 : 4); // bit in interrupt queue register of a certain priotiry level @@ -596,9 +600,9 @@ void duscc_channel::device_start() m_cid = (m_uart->m_variant & duscc_device::SET_CMOS) ? 0x7f : 0xff; // TODO: support CMOS rev A = 0xbf // Timers - duscc_timer = timer_alloc(TIMER_ID); - rtxc_timer = timer_alloc(TIMER_ID_RTXC); - trxc_timer = timer_alloc(TIMER_ID_TRXC); + duscc_timer = timer_alloc(FUNC(duscc_channel::check_zero_detect), this); + rtxc_timer = timer_alloc(FUNC(duscc_channel::rtxc_tick), this); + trxc_timer = timer_alloc(FUNC(duscc_channel::trxc_tick), this); // state saving save_item(NAME(m_cmr1)); @@ -723,95 +727,90 @@ void duscc_channel::device_reset() m_a7 = 0; } -void duscc_channel::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +TIMER_CALLBACK_MEMBER(duscc_channel::check_zero_detect) { - switch(id) + if (m_ct-- == 0) // Zero detect { - case TIMER_ID: - if (m_ct-- == 0) // Zero detect + m_ictsr |= REG_ICTSR_ZERO_DET; // set zero detection bit + + // Generate interrupt? + if ( ( (m_ctcr & REG_CTCR_ZERO_DET_INT) == REG_CTCR_ZERO_DET_INT ) && + ( (m_uart->m_icr & (m_index == duscc_device::CHANNEL_A ? duscc_device::REG_ICR_CHA : duscc_device::REG_ICR_CHB) ) != 0) ) { - m_ictsr |= REG_ICTSR_ZERO_DET; // set zero detection bit + LOG("Zero Detect Interrupt pending\n"); + m_uart->trigger_interrupt(m_index, INT_EXTCTSTAT); + } - // Generate interrupt? - if ( ( (m_ctcr & REG_CTCR_ZERO_DET_INT) == REG_CTCR_ZERO_DET_INT ) && - ( (m_uart->m_icr & (m_index == duscc_device::CHANNEL_A ? duscc_device::REG_ICR_CHA : duscc_device::REG_ICR_CHB) ) != 0) ) - { - LOG("Zero Detect Interrupt pending\n"); - m_uart->trigger_interrupt(m_index, INT_EXTCTSTAT); - } + // Preload or rollover? + if (( m_ctcr & REG_CTCR_ZERO_DET_CTL) == 0) + { + m_ct = m_ctpr; + } + else + { + m_ct = 0xffff; + } - // Preload or rollover? - if (( m_ctcr & REG_CTCR_ZERO_DET_CTL) == 0) + // Is Counter/Timer output on the RTxC pin? + if (( m_pcr & REG_PCR_RTXC_MASK) == REG_PCR_RTXC_CNTR_OUT) + { + if ((m_ctcr & REG_CTCR_TIM_OC) == 0) // Toggle? { - m_ct = m_ctpr; + m_rtxc = (~m_rtxc) & 1; } - else + else // Pulse! { - m_ct = 0xffff; + m_rtxc = 1; + rtxc_timer->adjust(attotime::from_hz(clock()), 0, attotime::from_hz(clock())); } + if (m_index == duscc_device::CHANNEL_A) + m_uart->m_out_rtxca_cb(m_rtxc); + else + m_uart->m_out_rtxcb_cb(m_rtxc); + } - // Is Counter/Timer output on the RTxC pin? - if (( m_pcr & REG_PCR_RTXC_MASK) == REG_PCR_RTXC_CNTR_OUT) + // Is Counter/Timer output on the TRXC pin? + if (( m_pcr & REG_PCR_TRXC_MASK) == REG_PCR_TRXC_CNTR_OUT) + { + if ((m_ctcr & REG_CTCR_TIM_OC) == 0) // Toggle? { - if ((m_ctcr & REG_CTCR_TIM_OC) == 0) // Toggle? - { - m_rtxc = (~m_rtxc) & 1; - } - else // Pulse! - { - m_rtxc = 1; - rtxc_timer->adjust(attotime::from_hz(clock()), TIMER_ID_RTXC, attotime::from_hz(clock())); - } - if (m_index == duscc_device::CHANNEL_A) - m_uart->m_out_rtxca_cb(m_rtxc); - else - m_uart->m_out_rtxcb_cb(m_rtxc); + m_trxc = (~m_trxc) & 1; } - - // Is Counter/Timer output on the TRXC pin? - if (( m_pcr & REG_PCR_TRXC_MASK) == REG_PCR_TRXC_CNTR_OUT) + else // Pulse! { - if ((m_ctcr & REG_CTCR_TIM_OC) == 0) // Toggle? - { - m_trxc = (~m_trxc) & 1; - } - else // Pulse! - { - m_trxc = 1; - trxc_timer->adjust(attotime::from_hz(clock()), TIMER_ID_TRXC, attotime::from_hz(clock())); - } - if (m_index == duscc_device::CHANNEL_A) - m_uart->m_out_trxca_cb(m_trxc); - else - m_uart->m_out_trxcb_cb(m_trxc); + m_trxc = 1; + trxc_timer->adjust(attotime::from_hz(clock()), 0, attotime::from_hz(clock())); } + if (m_index == duscc_device::CHANNEL_A) + m_uart->m_out_trxca_cb(m_trxc); + else + m_uart->m_out_trxcb_cb(m_trxc); } - else - { // clear zero detection bit - m_ictsr &= ~REG_ICTSR_ZERO_DET; - } - break; - case TIMER_ID_RTXC: // Terminate zero detection pulse - m_rtxc = 0; - rtxc_timer->adjust(attotime::never); - if (m_index == duscc_device::CHANNEL_A) - m_uart->m_out_rtxca_cb(m_rtxc); - else - m_uart->m_out_rtxcb_cb(m_rtxc); - break; - case TIMER_ID_TRXC: // Terminate zero detection pulse - m_trxc = 0; - trxc_timer->adjust(attotime::never); - if (m_index == duscc_device::CHANNEL_A) - m_uart->m_out_trxca_cb(m_trxc); - else - m_uart->m_out_trxcb_cb(m_trxc); - break; - default: - LOGR("Unhandled Timer ID %d\n", id); - break; } - // LOG("%s %d\n", FUNCNAME, id); + else + { // clear zero detection bit + m_ictsr &= ~REG_ICTSR_ZERO_DET; + } +} + +TIMER_CALLBACK_MEMBER(duscc_channel::rtxc_tick) +{ + m_rtxc = 0; + rtxc_timer->adjust(attotime::never); + if (m_index == duscc_device::CHANNEL_A) + m_uart->m_out_rtxca_cb(m_rtxc); + else + m_uart->m_out_rtxcb_cb(m_rtxc); +} + +TIMER_CALLBACK_MEMBER(duscc_channel::trxc_tick) +{ + m_trxc = 0; + trxc_timer->adjust(attotime::never); + if (m_index == duscc_device::CHANNEL_A) + m_uart->m_out_trxca_cb(m_trxc); + else + m_uart->m_out_trxcb_cb(m_trxc); } /* The DUSCC 16 bit Timer @@ -1288,7 +1287,7 @@ uint8_t duscc_channel::do_dusccreg_gsr_r() uint8_t duscc_channel::do_dusccreg_ier_r() { - LOGINT("%s <- %02x\n", FUNCNAME, m_ier); + LOGINT("%s(%02x)\n", FUNCNAME, m_ier); return (uint8_t) m_ier; } @@ -1955,6 +1954,7 @@ void duscc_channel::do_dusccreg_ccr_w(uint8_t data) m_tx_fifo_wp = m_tx_fifo_rp = 0; m_trsr &= 0x0f; m_uart->m_gsr &= ~(m_index == duscc_device::CHANNEL_A ? REG_GSR_CHAN_A_TXREADY : REG_GSR_CHAN_B_TXREADY); + m_uart->clear_interrupt(m_index, INT_TXREADY); break; /* Enable transmitter. Enables transmitter operation, conditioned by the state of @@ -1975,6 +1975,7 @@ void duscc_channel::do_dusccreg_ccr_w(uint8_t data) set_tra_rate(0); m_tra = 0; m_uart->m_gsr &= ~(m_index == duscc_device::CHANNEL_A ? REG_GSR_CHAN_A_TXREADY : REG_GSR_CHAN_B_TXREADY); + m_uart->clear_interrupt(m_index, INT_TXREADY); break; // RECEIVER COMMANDS @@ -1989,6 +1990,7 @@ void duscc_channel::do_dusccreg_ccr_w(uint8_t data) m_trsr &= 0xf0; m_rsr = 0; m_uart->m_gsr &= ~(m_index == duscc_device::CHANNEL_A ? REG_GSR_CHAN_A_RXREADY : REG_GSR_CHAN_B_RXREADY); + m_uart->clear_interrupt(m_index, INT_RXREADY); break; /* Enable receiver. Causes receiver operation to begin, conditioned by the state of the DCD @@ -2004,6 +2006,7 @@ void duscc_channel::do_dusccreg_ccr_w(uint8_t data) case REG_CCR_DISABLE_RX: LOGINT("- Disable Rx\n"); m_rcv = 0; m_uart->m_gsr &= ~(m_index == duscc_device::CHANNEL_A ? REG_GSR_CHAN_A_RXREADY : REG_GSR_CHAN_B_RXREADY); + m_uart->clear_interrupt(m_index, INT_RXREADY); break; // COUNTER/TIMER COMMANDS @@ -2011,7 +2014,7 @@ void duscc_channel::do_dusccreg_ccr_w(uint8_t data) /* Start. Starts the counteritimer and prescaler. */ case REG_CCR_START_TIMER: LOG("- Start Counter/Timer\n"); rate = 100; // TODO: calculate correct rate - duscc_timer->adjust(attotime::from_hz(rate), TIMER_ID_RTXC, attotime::from_hz(rate)); + duscc_timer->adjust(attotime::from_hz(rate), 0, attotime::from_hz(rate)); break; /* Stop. Stops the counter/timer and prescaler. Since the command may be asynchronous with the selected clock source, @@ -2039,7 +2042,7 @@ void duscc_channel::do_dusccreg_ccr_w(uint8_t data) void duscc_channel::do_dusccreg_txfifo_w(uint8_t data) { - LOGTX(" - TX %s(%02x)'%c'\n", FUNCNAME,data, isalnum(data) ? data : ' '); + LOGTX(" - TX %s(%02x)'%c' wp %d rp %d\n", FUNCNAME,data, isprint(data) ? data : ' ', m_tx_fifo_wp, m_tx_fifo_rp); /* Tx FIFO is full or...? */ if (m_tx_fifo_wp + 1 == m_tx_fifo_rp || ( (m_tx_fifo_wp + 1 == m_tx_fifo_sz) && (m_tx_fifo_rp == 0) )) @@ -2070,6 +2073,7 @@ void duscc_channel::do_dusccreg_txfifo_w(uint8_t data) if (m_tx_fifo_wp + 1 == m_tx_fifo_rp || ( (m_tx_fifo_wp + 1 == m_tx_fifo_sz) && (m_tx_fifo_rp == 0) )) { m_uart->m_gsr &= ~(m_index == duscc_device::CHANNEL_A ? REG_GSR_CHAN_A_TXREADY : REG_GSR_CHAN_B_TXREADY); + m_uart->clear_interrupt(m_index, INT_TXREADY); } else { @@ -2213,7 +2217,7 @@ uint8_t duscc_channel::read(offs_t &offset) { uint8_t data = 0; int reg = (offset | m_a7) & ~0x20; // Add extended rgisters and remove the channel B bit from offset - LOG("\"%s\" %s: %c : Register read '%02x' <- [%02x]", owner()->tag(), FUNCNAME, 'A' + m_index, data, reg ); + LOGR("\"%s\" %s: %c : Register read '%02x' <- [%02x]", owner()->tag(), FUNCNAME, 'A' + m_index, data, reg ); LOGR(" * %c Reg %02x -> %02x \n", 'A' + m_index, reg, data); switch (reg) { @@ -2264,7 +2268,7 @@ void duscc_channel::write(uint8_t data, offs_t &offset) int reg = (offset | m_a7) & ~0x20; // Add extended rgisters and remove the channel B bit from offset LOGSETUP(" * %s%c Reg %02x <- %02x \n", owner()->tag(), 'A' + m_index, reg, data); - LOG("\"%s\" %s: %c : Register write '%02x' -> [%02x]", owner()->tag(), FUNCNAME, 'A' + m_index, data, reg ); + LOG("\"%s\" %s: %c : Register write '%02x' -> [%02x]\n", owner()->tag(), FUNCNAME, 'A' + m_index, data, reg ); switch (reg) { case REG_CMR1: do_dusccreg_cmr1_w(data); break; @@ -2337,6 +2341,7 @@ void duscc_channel::m_rx_fifo_rp_step() // no more characters available in the FIFO LOGINT("Clear RXRDY in GSR because FIFO is emptied\n"); m_uart->m_gsr &= ~(m_index == duscc_device::CHANNEL_A ? REG_GSR_CHAN_A_RXREADY : REG_GSR_CHAN_B_RXREADY); + m_uart->clear_interrupt(m_index, INT_RXREADY); } } @@ -2418,6 +2423,7 @@ void duscc_channel::cts_w(int state) if (m_tpr & REG_TPR_CTS && m_tra) { m_uart->m_gsr |= (m_index == duscc_device::CHANNEL_A ? REG_GSR_CHAN_A_TXREADY : REG_GSR_CHAN_B_TXREADY); + m_uart->trigger_interrupt(m_index, INT_TXREADY); } // set clear to send |