summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/sdlc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/sdlc.cpp')
-rw-r--r--src/devices/machine/sdlc.cpp298
1 files changed, 298 insertions, 0 deletions
diff --git a/src/devices/machine/sdlc.cpp b/src/devices/machine/sdlc.cpp
new file mode 100644
index 00000000000..b78b5dd0055
--- /dev/null
+++ b/src/devices/machine/sdlc.cpp
@@ -0,0 +1,298 @@
+// 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_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_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);
+}
+
+void sdlc_logger_device::frame_abort()
+{
+ logerror("Frame aborted!\n");
+ shift_residual_bits();
+ log_frame(true);
+}
+
+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());
+ }
+}