diff options
Diffstat (limited to 'src/devices/machine/sci4.cpp')
-rw-r--r-- | src/devices/machine/sci4.cpp | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/src/devices/machine/sci4.cpp b/src/devices/machine/sci4.cpp new file mode 100644 index 00000000000..b8eb647b4a3 --- /dev/null +++ b/src/devices/machine/sci4.cpp @@ -0,0 +1,339 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +// Yamaha SCI4 / XV833A00, 7-lines serial chip with 4 multiplexed on one and the other 3 separated + +#include "emu.h" +#include "sci4.h" + +sci4_device::sci4_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, SCI4, tag, owner, clock), + m_tx(*this), + m_irq(*this) +{ +} + + +void sci4_device::map(address_map &map) +{ + map(0x00, 0x3f).rw(FUNC(sci4_device::default_r), FUNC(sci4_device::default_w)); + + map(0x00, 0x00).rw(FUNC(sci4_device::data_r), FUNC(sci4_device::data_w)).select(0x18); + map(0x01, 0x01).rw(FUNC(sci4_device::enable_r), FUNC(sci4_device::enable_w)).select(0x18); + map(0x02, 0x02).r (FUNC(sci4_device::status_r)).select(0x18); + map(0x03, 0x03).rw(FUNC(sci4_device::datamode_r), FUNC(sci4_device::datamode_w)).select(0x18); + map(0x05, 0x05).r (FUNC(sci4_device::reset_r)).select(0x18); + + map(0x20, 0x20).w (FUNC(sci4_device::target_w)); +} + + +void sci4_device::device_start() +{ + save_item(NAME(m_rx)); + save_item(NAME(m_enable)); + save_item(NAME(m_status)); + save_item(NAME(m_datamode)); + save_item(NAME(m_div)); + save_item(NAME(m_cur_rx)); + save_item(NAME(m_tdr)); + save_item(NAME(m_tsr)); + save_item(NAME(m_tdr_full)); + save_item(NAME(m_tx_step)); + save_item(NAME(m_tx_active)); + save_item(NAME(m_rdr)); + save_item(NAME(m_rsr)); + save_item(NAME(m_rdr_full)); + save_item(NAME(m_rx_step)); + save_item(NAME(m_rx_active)); + + save_item(NAME(m_targets)); + + for(u32 i=0; i != 4; i++) { + m_tx_timer[i] = timer_alloc(FUNC(sci4_device::tx_tick), this); + m_rx_timer[i] = timer_alloc(FUNC(sci4_device::rx_tick), this); + } +} + +void sci4_device::device_reset() +{ + std::fill(m_rx.begin(), m_rx.end(), 1); + std::fill(m_enable.begin(), m_enable.end(), 0); + std::fill(m_status.begin(), m_status.end(), 0); + std::fill(m_datamode.begin(), m_datamode.end(), 0); + std::fill(m_div.begin(), m_div.end(), 0); + std::fill(m_cur_rx.begin(), m_cur_rx.end(), 1); + std::fill(m_tdr.begin(), m_tdr.end(), 0); + std::fill(m_tsr.begin(), m_tsr.end(), 0); + std::fill(m_tdr_full.begin(), m_tdr_full.end(), 0); + std::fill(m_tx_step.begin(), m_tx_step.end(), 0); + std::fill(m_tx_active.begin(), m_tx_active.end(), 0); + std::fill(m_rdr.begin(), m_rdr.end(), 0); + std::fill(m_rsr.begin(), m_rsr.end(), 0); + std::fill(m_rdr_full.begin(), m_rdr_full.end(), 0); + std::fill(m_rx_step.begin(), m_rx_step.end(), 0); + std::fill(m_rx_active.begin(), m_rx_active.end(), 0); + + m_targets = 0; +} + +void sci4_device::do_rx_w(int sci, int state) +{ + if(sci >= 30) + sci = sci - 30 + 3; + + m_rx[sci] = state; + if(sci < 3) { + if(state != m_cur_rx[sci]) { + m_cur_rx[sci] = state; + rx_changed(sci); + } + } + u8 rx = ((((m_rx[6] << 3) | (m_rx[5] << 2) | (m_rx[4] << 1) | m_rx[3]) | ~(m_targets >> 4)) & 0xf) == 0xf; + if(rx != m_cur_rx[3]) { + m_cur_rx[3] = rx; + rx_changed(3); + } +} + +void sci4_device::default_w(offs_t offset, u8 data) +{ + logerror("reg_w %02x, %02x (%s)\n", offset, data, machine().describe_context()); +} + +u8 sci4_device::default_r(offs_t offset) +{ + logerror("reg_r %02x (%s)\n", offset, machine().describe_context()); + return 0; +} + +void sci4_device::datamode_w(offs_t slot, u8 data) +{ + m_datamode[slot >> 3] = data; +} + +u8 sci4_device::datamode_r(offs_t slot) +{ + return m_datamode[slot >> 3]; +} + +void sci4_device::data_w(offs_t slot, u8 data) +{ + slot >>= 3; + if(m_datamode[slot] == 0x80) { + m_div[slot] = data; + if(data) + logerror("channel %d baud rate %dHz\n", slot, clock()/16/data); + else + logerror("channel %d off\n"); + } else if(m_datamode[slot] & 2) + fifo_w(slot, data); +} + +u8 sci4_device::data_r(offs_t slot) +{ + slot >>= 3; + if(m_datamode[slot] == 0x80) + return m_div[slot]; + else if(m_datamode[slot] & 1) + return fifo_r(slot); + else + return 0; +} + +void sci4_device::enable_w(offs_t slot, u8 data) +{ + slot >>= 3; + u8 old = m_enable[slot]; + m_enable[slot] = data; + if((data & 2) && !(old & 2)) + tx_enabled(slot); + + else if(!(data & 2) && (old & 2)) { + if(m_status[slot] == 2) { + m_status[slot] = 0; + m_irq[slot](0); + } + } +} + +u8 sci4_device::enable_r(offs_t slot) +{ + return m_enable[slot >> 3]; +} + +u8 sci4_device::status_r(offs_t slot) +{ + return m_status[slot >> 3]; +} + +u8 sci4_device::reset_r(offs_t slot) +{ + slot >>= 3; + m_status[slot] = 0; + m_tx_timer[slot]->adjust(attotime::never); + m_tx_active[slot] = 0; + tx_set(slot, 1); + m_tdr_full[slot] = 0; + m_irq[slot](0); + return 0; +} + + +void sci4_device::target_w(u8 data) +{ + if(data == 0x11 && m_targets == 0x07) + machine().debug_break(); + m_targets = data; + u8 rx = ((((m_rx[6] << 3) | (m_rx[5] << 2) | (m_rx[4] << 1) | m_rx[3]) | ~(m_targets >> 4)) & 0xf) == 0xf; + if(rx != m_cur_rx[3]) { + m_cur_rx[3] = rx; + rx_changed(3); + } + for(u32 i=0; i != 4; i++) + if(!(m_targets & (1<<i))) + m_tx[i+3](1); +} + +void sci4_device::tx_set(int chan, int state) +{ + if(chan < 3) + m_tx[chan](state); + else + for(u32 i=0; i != 4; i++) + if((m_targets & (1<<i))) + m_tx[i+3](state); + +} + +void sci4_device::fifo_w(int chan, u8 data) +{ + if(m_tdr_full[chan] && (m_enable[chan] & 4)) { + m_status[chan] = 6; + m_irq[chan](1); + + } else { + m_tdr[chan] = data; + m_tdr_full[chan] = 1; + if(m_status[chan] == 2) { + m_status[chan] = 0; + m_irq[chan](0); + } + if(!m_tx_active[chan] && (m_enable[chan] & 2)) + tx_start(chan); + } +} + +u8 sci4_device::fifo_r(int chan) +{ + m_rdr_full[chan] = 0; + if(m_status[chan] == 4) { + m_status[chan] = 0; + m_irq[chan](0); + } + return m_rdr[chan]; +} + +void sci4_device::rx_changed(int chan) +{ + if(!m_rx_active[chan] && !m_cur_rx[chan] && (m_enable[chan] & 1)) { + m_rx_active[chan] = 1; + m_rx_step[chan] = 0; + m_rsr[chan] = 0; + wait(1, 0, chan); + } else if(m_rx_active[chan]) { + if(m_rx_step[chan] == 0) { + // Start bit gone before half-time + m_rx_active[chan] = 0; + m_rx_timer[chan]->adjust(attotime::never); + } else + // Force a precise resync + wait(1, 0, chan); + } +} + +void sci4_device::tx_enabled(int chan) +{ + if(m_tdr_full[chan]) + tx_start(chan); + else { + m_status[chan] |= 2; + m_irq[chan](1); + } +} + +std::string sci4_device::chan_id(u8 chan, u8 target) +{ + return chan < 3 ? util::string_format("%d", chan) : util::string_format("3:%s%s%s%s", + target & 1 ? "0" : "", + target & 2 ? "1" : "", + target & 4 ? "2" : "", + target & 8 ? "3" : ""); +} + +void sci4_device::tx_start(int chan) +{ + m_tx_active[chan] = 1; + m_tsr[chan] = m_tdr[chan]; + m_tdr_full[chan] = 0; + m_status[chan] |= 2; + m_irq[chan](1); + + logerror("chan %s transmit %02x\n", + chan_id(chan, m_targets), + m_tsr[chan]); + + tx_set(chan, 0); + m_tx_step[chan] = 0; + wait(0, 1, chan); +} + +void sci4_device::wait(int timer, int full, int chan) +{ + u32 div = m_div[chan] ? m_div[chan] : 0x100; + u32 cycles = div*(full ? 16 : 8); + (timer ? m_rx_timer : m_tx_timer)[chan]->adjust(attotime::from_ticks(cycles, clock()), chan); +} + +TIMER_CALLBACK_MEMBER(sci4_device::tx_tick) +{ + u32 step = m_tx_step[param]++; + if(step < 9) { + tx_set(param, step == 8 ? 1 : (m_tsr[param] >> step) & 1); + wait(0, 1, param); + + } else { + if((m_enable[param] & 2) && m_tdr_full[param]) + tx_start(param); + else + m_tx_active[param] = 0; + } +} + +TIMER_CALLBACK_MEMBER(sci4_device::rx_tick) +{ + u32 step = m_rx_step[param]++; + if(step == 0) + wait(1, 1, param); // Value already checked in rx_changed + else if(step < 9) { + if(m_cur_rx[param]) + m_rsr[param] |= 1 << (step - 1); + wait(1, 1, param); + + } else { + if(!m_rx[param]) + logerror("chan %s framing error/break\n", chan_id(param, m_targets >> 4)); + else { + logerror("chan %s recieved %02x\n", chan_id(param, m_targets >> 4), m_rsr[param]); + m_rx_active[param] = 0; + m_rdr[param] = m_rsr[param]; + if(m_rdr_full[param] && (m_enable[param] & 4)) + m_status[param] = 6; + else + m_status[param] = 4; + m_irq[param](1); + } + } +} + +DEFINE_DEVICE_TYPE(SCI4, sci4_device, "sci4", "Yamaha SCI4 quad-serial gate array") |