diff options
Diffstat (limited to 'src/devices/machine/sdlc.cpp')
-rw-r--r-- | src/devices/machine/sdlc.cpp | 302 |
1 files changed, 302 insertions, 0 deletions
diff --git a/src/devices/machine/sdlc.cpp b/src/devices/machine/sdlc.cpp new file mode 100644 index 00000000000..e3cc8cc99ed --- /dev/null +++ b/src/devices/machine/sdlc.cpp @@ -0,0 +1,302 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb + +#include "emu.h" +#include "sdlc.h" + +#include <sstream> + + +#define LOG_GENERAL (1U << 0) +#define LOG_RXBIT (1U << 1) +#define LOG_RXFLAG (1U << 2) +#define LOG_LINESTATE (1U << 3) +#define LOG_FRAMING (1U << 4) + +//#define VERBOSE (LOG_GENERAL | LOG_RXBIT | LOG_RXFLAG | LOG_LINESTATE | LOG_FRAMING) +#include "logmacro.h" + +#define LOGRXBIT(...) LOGMASKED(LOG_RXBIT, __VA_ARGS__) +#define LOGRXFLAG(...) LOGMASKED(LOG_RXFLAG, __VA_ARGS__) +#define LOGLINESTATE(...) LOGMASKED(LOG_LINESTATE, __VA_ARGS__) +#define LOGFRAMING(...) LOGMASKED(LOG_FRAMING, __VA_ARGS__) + + +DEFINE_DEVICE_TYPE(SDLC_LOGGER, sdlc_logger_device, "sdlc_logger", "SDLC/HDLC logger") + + +constexpr std::uint16_t device_sdlc_consumer_interface::POLY_SDLC; + + +device_sdlc_consumer_interface::device_sdlc_consumer_interface(machine_config const &mconfig, device_t &device) : + device_interface(device, "sdlc_consumer"), + m_line_active(0U), + m_discard_bits(0U), + m_in_frame(0U), + m_shift_register(0xffffU), + m_frame_check(0xffffU) +{ +} + +void device_sdlc_consumer_interface::interface_post_start() +{ + device().save_item(NAME(m_line_active)); + device().save_item(NAME(m_discard_bits)); + device().save_item(NAME(m_in_frame)); + device().save_item(NAME(m_shift_register)); + device().save_item(NAME(m_frame_check)); +} + +void device_sdlc_consumer_interface::rx_bit(bool state) +{ + LOGRXBIT("Received bit %u\n", state ? 1U : 0U); + + m_shift_register = (m_shift_register >> 1) | (state ? 0x8000U : 0x0000U); + if (!state && !m_line_active) + { + // any zero bit means the line has become active + LOGLINESTATE("Line became active\n"); + m_line_active = 1U; + line_active(); + } + + if ((m_shift_register & 0xff00U) == 0x7e00U) + { + // a flag opens and closes frames + LOGRXFLAG("Received flag\n"); + if (m_in_frame) + { + LOGFRAMING("End of frame\n"); + m_in_frame = 0U; + frame_end(); + } + m_discard_bits = 8U; + m_frame_check = 0xffffU; + } + else if ((m_shift_register & 0xfffeU) == 0xfffeU) + { + // fifteen consecutive ones is an inactive line condition + if (m_line_active) + { + LOGLINESTATE("Line became inactive\n"); + m_line_active = 0U; + line_inactive(); + } + } + else if ((m_shift_register & 0xfe00U) == 0xfe00U) + { + // seven consecutive ones is a frame abort + if (m_in_frame || m_discard_bits) + { + LOGFRAMING("Received frame abort\n"); + m_in_frame = 0U; + m_discard_bits = 0U; + frame_abort(); + } + } + else + { + // discard the flag as it shifts off + if (m_discard_bits && !--m_discard_bits) + { + LOGFRAMING("Start of frame\n"); + m_in_frame = 1U; + frame_start(); + } + + // discard a zero after five consecutive ones + if (m_in_frame && ((m_shift_register & 0x01f8U) != 0x00f8U)) + { + bool const bit(BIT(m_shift_register, 8)); + m_frame_check = update_frame_check(POLY_SDLC, m_frame_check, bit); + data_bit(bit); + } + } +} + +void device_sdlc_consumer_interface::rx_reset() +{ + LOG("Receive reset\n"); + + m_line_active = 0U; + m_in_frame = 0U; + m_discard_bits = 0U; + m_shift_register = 0xffffU; + m_frame_check = 0xffffU; +} + + +sdlc_logger_device::sdlc_logger_device(machine_config const &mconfig, char const *tag, device_t *owner, std::uint32_t clock) : + device_t(mconfig, SDLC_LOGGER, tag, owner, clock), + device_sdlc_consumer_interface(mconfig, *this), + m_data_nrzi(0U), + m_clock_active(1U), + m_current_data(1U), + m_last_data(1U), + m_current_clock(1U), + m_frame_bits(0U), + m_expected_fcs(0U), + m_buffer() +{ +} + +WRITE_LINE_MEMBER(sdlc_logger_device::clock_w) +{ + if (bool(state) != bool(m_current_clock)) + { + m_current_clock = state ? 1U : 0U; + if (m_current_clock == m_clock_active) + { + bool const bit(m_data_nrzi ? (m_current_data == m_last_data) : m_current_data); + LOGRXBIT("Received bit: %u (%u -> %u)\n", bit ? 1U : 0U, m_last_data, m_current_data); + m_last_data = m_current_data; + rx_bit(bit); + } + } +} + +void sdlc_logger_device::device_start() +{ + m_buffer.reset(new std::uint8_t[BUFFER_BYTES]); + + save_item(NAME(m_data_nrzi)); + save_item(NAME(m_clock_active)); + save_item(NAME(m_current_data)); + save_item(NAME(m_last_data)); + save_item(NAME(m_current_clock)); + save_item(NAME(m_frame_bits)); + save_item(NAME(m_expected_fcs)); + save_pointer(NAME(m_buffer.get()), BUFFER_BYTES); +} + +void sdlc_logger_device::device_reset() +{ +} + +void sdlc_logger_device::frame_start() +{ + m_frame_bits = 0U; + m_expected_fcs = 0xffffU; +} + +void sdlc_logger_device::frame_end() +{ + shift_residual_bits(); + log_frame(false); + m_frame_bits = 0; +} + +void sdlc_logger_device::frame_abort() +{ + logerror("Frame aborted!\n"); + shift_residual_bits(); + log_frame(true); + m_frame_bits = 0U; +} + +void sdlc_logger_device::data_bit(bool value) +{ + if (BUFFER_BITS > m_frame_bits) + { + m_buffer[m_frame_bits >> 3] >>= 1; + m_buffer[m_frame_bits >> 3] |= value ? 0x80U : 0x00U; + } + else if (BUFFER_BITS == m_frame_bits) + { + logerror("Frame buffer overrun!\n"); + } + + if ((16U <= m_frame_bits) && ((BUFFER_BITS + 16U) > m_frame_bits)) + m_expected_fcs = update_frame_check(POLY_SDLC, m_expected_fcs, BIT(m_buffer[(m_frame_bits - 16U) >> 3], m_frame_bits & 0x0007U)); + + ++m_frame_bits; +} + +void sdlc_logger_device::shift_residual_bits() +{ + if (BUFFER_BITS > m_frame_bits) + { + uint32_t const residual_bits(m_frame_bits & 0x0007U); + if (residual_bits) + m_buffer[m_frame_bits >> 3] >>= 8 - residual_bits; + } +} + +void sdlc_logger_device::log_frame(bool partial) const +{ + if (m_frame_bits) + { + std::ostringstream msg; + std::uint32_t const frame_bytes(m_frame_bits >> 3); + std::uint32_t const residual_bits(m_frame_bits & 0x0007U); + util::stream_format(msg, "Received %u-bit %sframe (%u bytes + %u bits)", m_frame_bits, partial ? "partial " : "", frame_bytes, residual_bits); + + if (8U <= m_frame_bits) + { + std::uint8_t const addr(m_buffer[0]); + util::stream_format(msg, " A=%02X%s", addr, (0xffU == addr) ? " (broadcast)" : !addr ? " (no station)" : ""); + } + + if (16U <= m_frame_bits) + { + std::uint8_t const ctrl(m_buffer[1]); + if (!BIT(ctrl, 0)) + { + msg << " I"; + } + else if (!BIT(ctrl, 1)) + { + msg << " S"; + switch (ctrl & 0x0cU) + { + case 0x00U: msg << " RR"; break; + case 0x04U: msg << " RNR"; break; + case 0x08U: msg << " REJ"; break; + } + } + else + { + msg << " U"; + switch (ctrl & 0xecU) + { + case 0x00U: msg << " UI"; break; + case 0x04U: msg << " RIM/SIM"; break; + case 0x0cU: msg << " DM"; break; + case 0x20U: msg << " UP"; break; + case 0x40U: msg << " DISC/RD"; break; + case 0x60U: msg << " UA"; break; + case 0x80U: msg << " SNRM"; break; + case 0x84U: msg << " FRMR"; break; + case 0x9cU: msg << " XID"; break; + case 0xc4U: msg << " CFGR"; break; + case 0xccU: msg << " SNRME"; break; + case 0xe0U: msg << " TEST"; break; + case 0xecU: msg << " BCN"; break; + } + } + + if (!partial && (BUFFER_BITS >= m_frame_bits)) + { + std::uint16_t fcs; + fcs = std::uint16_t(m_buffer[frame_bytes - 2]) >> residual_bits; + fcs |= std::uint16_t(m_buffer[frame_bytes - 1]) << (8 - residual_bits); + if (residual_bits) + fcs |= (std::uint16_t(m_buffer[frame_bytes]) & ((1U << residual_bits) - 1U)) << (16 - residual_bits); + fcs = ~BITSWAP16(fcs, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + util::stream_format(msg, " FCS=%04X", fcs); + if (!is_frame_check_good()) + util::stream_format(msg, " (expected %04X)", m_expected_fcs); + } + } + + if (!partial) + msg << (is_frame_check_good() ? " (good)" : " (bad)"); + + for (std::uint32_t i = 0U; (frame_bytes > i) && (BUFFER_BYTES > i); ++i) + util::stream_format(msg, (i & 0x000fU) ? " %02X" : "\n %02X", m_buffer[i]); + if (residual_bits && (BUFFER_BITS >= m_frame_bits)) + util::stream_format(msg, (residual_bits > 4) ? "%s %02X&%02X" : "%s %01X&%01X", (frame_bytes & 0x000fU) ? "" : "\n ", m_buffer[frame_bytes], (1U << residual_bits) - 1); + + logerror("%s\n", msg.str()); + } +} |