diff options
Diffstat (limited to 'src/devices/cpu/z180/z180csio.cpp')
-rw-r--r-- | src/devices/cpu/z180/z180csio.cpp | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/src/devices/cpu/z180/z180csio.cpp b/src/devices/cpu/z180/z180csio.cpp new file mode 100644 index 00000000000..21e9ae0b1e5 --- /dev/null +++ b/src/devices/cpu/z180/z180csio.cpp @@ -0,0 +1,254 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb, Sandro Ronco, Miodrag Milanovic +/********************************************************************* + + z180csio.cpp + +*********************************************************************/ + +/********************************************************************* + + TODO: + - Handle entering IOSTOP mode. + - Handle mid-transfer clock frequency changes. + +*********************************************************************/ + +#include "emu.h" +#include "z180.h" + +//#define VERBOSE 1 +#include "logmacro.h" + + +// 0x0a CSI/O control/status register (EF is read-only) +static constexpr u8 Z180_CNTR_EF = 0x80; +static constexpr u8 Z180_CNTR_EIE = 0x40; +static constexpr u8 Z180_CNTR_RE = 0x20; +static constexpr u8 Z180_CNTR_TE = 0x10; +static constexpr u8 Z180_CNTR_SS = 0x07; + +static constexpr u8 Z180_CNTR_MASK = 0xf7; + + +//************************************************************************** +// z180csio_device +//************************************************************************** + +z180csio_device::z180csio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : device_t(mconfig, Z180CSIO, tag, owner, clock) + , m_cks_cb(*this) + , m_txs_cb(*this) + , m_internal_clock(nullptr) + , m_cntr(0) + , m_trdr(0) + , m_shift_cnt(0) + , m_irq(0) + , m_cks_in(1) + , m_rxs_in(1) + , m_cks_out(1) + , m_txs_out(1) +{ +} + + +void z180csio_device::device_resolve_objects() +{ + // set default input line state + m_cks_in = 1; + m_rxs_in = 1; +} + + +void z180csio_device::device_start() +{ + // TRDR is not affected by reset - set it here to make behaviour deterministic + m_trdr = 0; + + save_item(NAME(m_cntr)); + save_item(NAME(m_trdr)); + save_item(NAME(m_shift_cnt)); + save_item(NAME(m_irq)); + save_item(NAME(m_cks_in)); + save_item(NAME(m_rxs_in)); + save_item(NAME(m_cks_out)); + save_item(NAME(m_txs_out)); + + m_internal_clock = timer_alloc(timer_expired_delegate(FUNC(z180csio_device::internal_clock), this)); +} + + +void z180csio_device::device_reset() +{ + m_cntr = 0x07; + m_shift_cnt = 0; + m_irq = 0; + m_cks_out = 1; + m_txs_out = 1; // TODO: is this affected by reset? + + m_internal_clock->adjust(attotime::never); + + m_cks_cb(m_cks_out); + m_txs_cb(m_txs_out); +} + + +void z180csio_device::state_add(device_state_interface &parent) +{ + parent.state_add(Z180_CNTR, "CNTR", m_cntr).mask(Z180_CNTR_MASK); + parent.state_add(Z180_TRDR, "TRDR", m_trdr); +} + + +u8 z180csio_device::cntr_r() +{ + LOG("Z180 CNTR rd $%02x\n", m_cntr); + return m_cntr & Z180_CNTR_MASK; +} + + +u8 z180csio_device::trdr_r() +{ + // TODO: from manual page 47: "Program access of TRDR only occurs if EF = 1." + // Should access be suppressed if EF is clear? + + LOG("Z180 TRDR rd $%02x\n", m_trdr); + if (!machine().side_effects_disabled()) + m_cntr &= ~Z180_CNTR_EF; + + return m_trdr; +} + + +void z180csio_device::cntr_w(u8 data) +{ + // TODO: + // From manual page 47: "TE and RE are never both set to 1 at the same time." + // If one attempts to write 1 to both at the same time, which takes precedence? + + LOG("Z180 CNTR wr $%02x\n", data); + + if (data & (Z180_CNTR_RE | Z180_CNTR_TE)) + { + // if receive or transmit will be enabled, start clock if necessary + if (!(m_cntr & (Z180_CNTR_RE | Z180_CNTR_TE))) + { + if ((data & Z180_CNTR_SS) != 7) + m_internal_clock->adjust(attotime::from_hz(clock())); + else + m_internal_clock->adjust(attotime::never); + } + } + else + { + m_shift_cnt = 0; + if (!m_cks_out) + { + // this probably takes at least one clock to take actually happen + m_cks_out = 1; + if ((data & Z180_CNTR_SS) != 7) + m_cks_cb(1); + } + m_internal_clock->adjust(attotime::never); + } + + // TODO: if switching internal/external clock, update CKS output and trigger a clock edge if necessary + + m_cntr = (m_cntr & Z180_CNTR_EF) | (data & ~Z180_CNTR_EF & Z180_CNTR_MASK); // EF is read-only +} + + +void z180csio_device::trdr_w(u8 data) +{ + // TODO: from manual page 47: "Program access of TRDR only occurs if EF = 1." + // Should access be suppressed if EF is clear? + + LOG("Z180 TRDR wr $%02x\n", data); + m_cntr &= ~Z180_CNTR_EF; + m_trdr = data; +} + + +TIMER_CALLBACK_MEMBER(z180csio_device::internal_clock) +{ + if ((m_cntr & Z180_CNTR_SS) != 7) + { + m_cks_out ^= 1; + m_cks_cb(m_cks_out); + clock_edge(m_cks_out); + + if (m_cntr & (Z180_CNTR_RE | Z180_CNTR_TE)) + { + int div; + switch (m_cntr & Z180_CNTR_SS) + { + default: // just to pacify compilers + case 0: div = 20; break; + case 1: div = 40; break; + case 2: div = 80; break; + case 3: div = 160; break; + case 4: div = 320; break; + case 5: div = 640; break; + case 6: div = 1280; break; + } + m_internal_clock->adjust(attotime::from_hz(clock() / div)); + } + } +} + + +void z180csio_device::cks_wr(int state) +{ + state = state ? 1 : 0; + if (m_cks_in != state) + { + m_cks_in = state; + if ((m_cntr & Z180_CNTR_SS) == 0x07) + clock_edge(m_cks_in); + } +} + + +void z180csio_device::rxs_wr(int state) +{ + m_rxs_in = state ? 1 : 0; +} + + +void z180csio_device::clock_edge(u8 cks) +{ + if (!cks) + { + // TXS updated on falling edge + if (m_cntr & Z180_CNTR_TE) + { + u8 const txs = BIT(m_trdr, m_shift_cnt); + if (m_txs_out != txs) + { + m_txs_out = txs; + m_txs_cb(txs); + } + } + } + else + { + // Sample RXS on rising edge (ignore minimum setup/sampling time) + if (m_cntr & Z180_CNTR_RE) + m_trdr = (m_trdr & ~(u8(1) << m_shift_cnt)) | (m_rxs_in << m_shift_cnt); + + // EF/RE/TE updated on rising edge + if (m_cntr & (Z180_CNTR_RE | Z180_CNTR_TE)) + { + m_shift_cnt = (m_shift_cnt + 1) & 7; + if (!m_shift_cnt) + m_cntr = Z180_CNTR_EF | (m_cntr & ~(Z180_CNTR_RE | Z180_CNTR_TE)); + } + } +} + + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** + +DEFINE_DEVICE_TYPE(Z180CSIO, z180csio_device, "z180csio", "Z180 CSIO") |