1 // license:BSD-3-Clause
2 // copyright-holders:Vas Crabb
3 
4 #include "emu.h"
5 #include "sdlc.h"
6 
7 #include <sstream>
8 
9 
10 #define LOG_GENERAL     (1U << 0)
11 #define LOG_RXBIT       (1U << 1)
12 #define LOG_RXFLAG      (1U << 2)
13 #define LOG_LINESTATE   (1U << 3)
14 #define LOG_FRAMING     (1U << 4)
15 
16 //#define VERBOSE (LOG_GENERAL | LOG_RXBIT | LOG_RXFLAG | LOG_LINESTATE | LOG_FRAMING)
17 #include "logmacro.h"
18 
19 #define LOGRXBIT(...)       LOGMASKED(LOG_RXBIT, __VA_ARGS__)
20 #define LOGRXFLAG(...)      LOGMASKED(LOG_RXFLAG, __VA_ARGS__)
21 #define LOGLINESTATE(...)   LOGMASKED(LOG_LINESTATE, __VA_ARGS__)
22 #define LOGFRAMING(...)     LOGMASKED(LOG_FRAMING, __VA_ARGS__)
23 
24 
25 DEFINE_DEVICE_TYPE(SDLC_LOGGER, sdlc_logger_device, "sdlc_logger", "SDLC/HDLC logger")
26 
27 
28 constexpr std::uint16_t device_sdlc_consumer_interface::POLY_SDLC;
29 
30 
device_sdlc_consumer_interface(machine_config const & mconfig,device_t & device)31 device_sdlc_consumer_interface::device_sdlc_consumer_interface(machine_config const &mconfig, device_t &device) :
32 	device_interface(device, "sdlc_consumer"),
33 	m_line_active(0U),
34 	m_discard_bits(0U),
35 	m_in_frame(0U),
36 	m_shift_register(0xffffU),
37 	m_frame_check(0xffffU)
38 {
39 }
40 
interface_post_start()41 void device_sdlc_consumer_interface::interface_post_start()
42 {
43 	device().save_item(NAME(m_line_active));
44 	device().save_item(NAME(m_discard_bits));
45 	device().save_item(NAME(m_in_frame));
46 	device().save_item(NAME(m_shift_register));
47 	device().save_item(NAME(m_frame_check));
48 }
49 
rx_bit(bool state)50 void device_sdlc_consumer_interface::rx_bit(bool state)
51 {
52 	LOGRXBIT("Received bit %u\n", state ? 1U : 0U);
53 
54 	m_shift_register = (m_shift_register >> 1) | (state ? 0x8000U : 0x0000U);
55 	if (!state && !m_line_active)
56 	{
57 		// any zero bit means the line has become active
58 		LOGLINESTATE("Line became active\n");
59 		m_line_active = 1U;
60 		line_active();
61 	}
62 
63 	if ((m_shift_register & 0xff00U) == 0x7e00U)
64 	{
65 		// a flag opens and closes frames
66 		LOGRXFLAG("Received flag\n");
67 		if (m_in_frame)
68 		{
69 			LOGFRAMING("End of frame\n");
70 			m_in_frame = 0U;
71 			frame_end();
72 		}
73 		m_discard_bits = 8U;
74 		m_frame_check = 0xffffU;
75 	}
76 	else if ((m_shift_register & 0xfffeU) == 0xfffeU)
77 	{
78 		// fifteen consecutive ones is an inactive line condition
79 		if (m_line_active)
80 		{
81 			LOGLINESTATE("Line became inactive\n");
82 			m_line_active = 0U;
83 			line_inactive();
84 		}
85 	}
86 	else if ((m_shift_register & 0xfe00U) == 0xfe00U)
87 	{
88 		// seven consecutive ones is a frame abort
89 		if (m_in_frame || m_discard_bits)
90 		{
91 			LOGFRAMING("Received frame abort\n");
92 			m_in_frame = 0U;
93 			m_discard_bits = 0U;
94 			frame_abort();
95 		}
96 	}
97 	else
98 	{
99 		// discard the flag as it shifts off
100 		if (m_discard_bits && !--m_discard_bits)
101 		{
102 			LOGFRAMING("Start of frame\n");
103 			m_in_frame = 1U;
104 			frame_start();
105 		}
106 
107 		// discard a zero after five consecutive ones
108 		if (m_in_frame && ((m_shift_register & 0x01f8U) != 0x00f8U))
109 		{
110 			bool const bit(BIT(m_shift_register, 8));
111 			m_frame_check = update_frame_check(POLY_SDLC, m_frame_check, bit);
112 			data_bit(bit);
113 		}
114 	}
115 }
116 
rx_reset()117 void device_sdlc_consumer_interface::rx_reset()
118 {
119 	LOG("Receive reset\n");
120 
121 	m_line_active = 0U;
122 	m_in_frame = 0U;
123 	m_discard_bits = 0U;
124 	m_shift_register = 0xffffU;
125 	m_frame_check = 0xffffU;
126 }
127 
128 
sdlc_logger_device(machine_config const & mconfig,char const * tag,device_t * owner,std::uint32_t clock)129 sdlc_logger_device::sdlc_logger_device(machine_config const &mconfig, char const *tag, device_t *owner, std::uint32_t clock) :
130 	device_t(mconfig, SDLC_LOGGER, tag, owner, clock),
131 	device_sdlc_consumer_interface(mconfig, *this),
132 	m_data_nrzi(0U),
133 	m_clock_active(1U),
134 	m_current_data(1U),
135 	m_last_data(1U),
136 	m_current_clock(1U),
137 	m_frame_bits(0U),
138 	m_expected_fcs(0U),
139 	m_buffer()
140 {
141 }
142 
WRITE_LINE_MEMBER(sdlc_logger_device::clock_w)143 WRITE_LINE_MEMBER(sdlc_logger_device::clock_w)
144 {
145 	if (bool(state) != bool(m_current_clock))
146 	{
147 		m_current_clock = state ? 1U : 0U;
148 		if (m_current_clock == m_clock_active)
149 		{
150 			bool const bit(m_data_nrzi ? (m_current_data == m_last_data) : m_current_data);
151 			LOGRXBIT("Received bit: %u (%u -> %u)\n", bit ? 1U : 0U, m_last_data, m_current_data);
152 			m_last_data = m_current_data;
153 			rx_bit(bit);
154 		}
155 	}
156 }
157 
device_start()158 void sdlc_logger_device::device_start()
159 {
160 	m_buffer.reset(new std::uint8_t[BUFFER_BYTES]);
161 
162 	save_item(NAME(m_data_nrzi));
163 	save_item(NAME(m_clock_active));
164 	save_item(NAME(m_current_data));
165 	save_item(NAME(m_last_data));
166 	save_item(NAME(m_current_clock));
167 	save_item(NAME(m_frame_bits));
168 	save_item(NAME(m_expected_fcs));
169 	save_pointer(NAME(m_buffer), BUFFER_BYTES);
170 }
171 
device_reset()172 void sdlc_logger_device::device_reset()
173 {
174 }
175 
frame_start()176 void sdlc_logger_device::frame_start()
177 {
178 	m_frame_bits = 0U;
179 	m_expected_fcs = 0xffffU;
180 }
181 
frame_end()182 void sdlc_logger_device::frame_end()
183 {
184 	shift_residual_bits();
185 	log_frame(false);
186 	m_frame_bits = 0;
187 }
188 
frame_abort()189 void sdlc_logger_device::frame_abort()
190 {
191 	logerror("Frame aborted!\n");
192 	shift_residual_bits();
193 	log_frame(true);
194 	m_frame_bits = 0U;
195 }
196 
data_bit(bool value)197 void sdlc_logger_device::data_bit(bool value)
198 {
199 	if (BUFFER_BITS > m_frame_bits)
200 	{
201 		m_buffer[m_frame_bits >> 3] >>= 1;
202 		m_buffer[m_frame_bits >> 3] |= value ? 0x80U : 0x00U;
203 	}
204 	else if (BUFFER_BITS == m_frame_bits)
205 	{
206 		logerror("Frame buffer overrun!\n");
207 	}
208 
209 	if ((16U <= m_frame_bits) && ((BUFFER_BITS + 16U) > m_frame_bits))
210 		m_expected_fcs = update_frame_check(POLY_SDLC, m_expected_fcs, BIT(m_buffer[(m_frame_bits - 16U) >> 3], m_frame_bits & 0x0007U));
211 
212 	++m_frame_bits;
213 }
214 
shift_residual_bits()215 void sdlc_logger_device::shift_residual_bits()
216 {
217 	if (BUFFER_BITS > m_frame_bits)
218 	{
219 		uint32_t const residual_bits(m_frame_bits & 0x0007U);
220 		if (residual_bits)
221 			m_buffer[m_frame_bits >> 3] >>= 8 - residual_bits;
222 	}
223 }
224 
log_frame(bool partial) const225 void sdlc_logger_device::log_frame(bool partial) const
226 {
227 	if (m_frame_bits)
228 	{
229 		std::ostringstream msg;
230 		std::uint32_t const frame_bytes(m_frame_bits >> 3);
231 		std::uint32_t const residual_bits(m_frame_bits & 0x0007U);
232 		util::stream_format(msg, "Received %u-bit %sframe (%u bytes + %u bits)", m_frame_bits, partial ? "partial " : "", frame_bytes, residual_bits);
233 
234 		if (8U <= m_frame_bits)
235 		{
236 			std::uint8_t const addr(m_buffer[0]);
237 			util::stream_format(msg, " A=%02X%s", addr, (0xffU == addr) ? " (broadcast)" : !addr ? " (no station)" : "");
238 		}
239 
240 		if (16U <= m_frame_bits)
241 		{
242 			std::uint8_t const ctrl(m_buffer[1]);
243 			if (!BIT(ctrl, 0))
244 			{
245 				msg << " I";
246 			}
247 			else if (!BIT(ctrl, 1))
248 			{
249 				msg << " S";
250 				switch (ctrl & 0x0cU)
251 				{
252 				case 0x00U: msg << " RR"; break;
253 				case 0x04U: msg << " RNR"; break;
254 				case 0x08U: msg << " REJ"; break;
255 				}
256 			}
257 			else
258 			{
259 				msg << " U";
260 				switch (ctrl & 0xecU)
261 				{
262 				case 0x00U: msg << " UI"; break;
263 				case 0x04U: msg << " RIM/SIM"; break;
264 				case 0x0cU: msg << " DM"; break;
265 				case 0x20U: msg << " UP"; break;
266 				case 0x40U: msg << " DISC/RD"; break;
267 				case 0x60U: msg << " UA"; break;
268 				case 0x80U: msg << " SNRM"; break;
269 				case 0x84U: msg << " FRMR"; break;
270 				case 0x9cU: msg << " XID"; break;
271 				case 0xc4U: msg << " CFGR"; break;
272 				case 0xccU: msg << " SNRME"; break;
273 				case 0xe0U: msg << " TEST"; break;
274 				case 0xecU: msg << " BCN"; break;
275 				}
276 			}
277 
278 			if (!partial && (BUFFER_BITS >= m_frame_bits))
279 			{
280 				std::uint16_t fcs;
281 				fcs = std::uint16_t(m_buffer[frame_bytes - 2]) >> residual_bits;
282 				fcs |= std::uint16_t(m_buffer[frame_bytes - 1]) << (8 - residual_bits);
283 				if (residual_bits)
284 					fcs |= (std::uint16_t(m_buffer[frame_bytes]) & ((1U << residual_bits) - 1U)) << (16 - residual_bits);
285 				fcs = ~bitswap<16>(fcs, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
286 				util::stream_format(msg, " FCS=%04X", fcs);
287 				if (!is_frame_check_good())
288 					util::stream_format(msg, " (expected %04X)", m_expected_fcs);
289 			}
290 		}
291 
292 		if (!partial)
293 			msg << (is_frame_check_good() ? " (good)" : " (bad)");
294 
295 		for (std::uint32_t i = 0U; (frame_bytes > i) && (BUFFER_BYTES > i); ++i)
296 			util::stream_format(msg, (i & 0x000fU) ? " %02X" : "\n    %02X", m_buffer[i]);
297 		if (residual_bits && (BUFFER_BITS >= m_frame_bits))
298 			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);
299 
300 		logerror("%s\n", msg.str());
301 	}
302 }
303