1 // license:BSD-3-Clause
2 // copyright-holders: F. Ulivi
3 /*********************************************************************
4 
5     remote488.cpp
6 
7     This device allows the interfacing of a local IEEE-488 bus with
8     external devices (i.e. outside of MAME environment).
9     It's based on a simple text based protocol. The protocol relies
10     on a bidirectional stream-based connection (a bitbanger).
11     Main design features of the protocol:
12     - Transparent to data & commands exchanged
13     - Transparent to remote/local position of controller & controlled
14       devices
15     - Can be cross-connected: by routing the output direction of a
16       running MAME instance into the input direction of another instance
17       and viceversa, the two buses form a single logical bus.
18 
19     Protocol exchanges messages that are composed of a single uppercase
20     letter, a colon, a byte value expressed as 2 hex digits and a terminator
21     character.
22     Valid terminator characters are ',' or ';' or whitespace. Extra
23     whitespace before and after the message is ignored.
24     The letter encodes the type of the message and the byte has different
25     meaning according to type of message. Not all message types carry
26     a meaningful byte value (but for uniformity the byte is always sent).
27     Example of a message: "D:55 " (byte with 0x55 value exchanged on the
28     bus without EOI being asserted).
29     Example of a sequence of messages: D:AA,D:55,E:00 or
30     D:AA\nD:55\nE:00.
31 
32     The following table summarizes the various message types, the directions
33     wrt the remotizer in which they are meaningful, whether the byte
34     carries a value and the purpose of the message.
35 
36     | Type | Direction | Has byte? | Meaning                         |
37     |------+-----------+-----------+---------------------------------|
38     | D    | I/O       | Yes       | Non-EOI DAB or command byte     |
39     | E    | I/O       | Yes       | EOI DAB                         |
40     | J    | O         | No        | Echo request                    |
41     | K    | I         | No        | Echo reply                      |
42     | P    | I         | Yes       | Set byte for next parallel poll |
43     | Q    | O         | No        | Request 'P' msg                 |
44     | R    | I/O       | Yes       | Set bus control signals to 0    |
45     | S    | I/O       | Yes       | Set bus control signals to 1    |
46     | X    | I/O       | No        | Checkpoint in byte string       |
47     | Y    | I/O       | Yes       | Checkpoint reached              |
48 
49     - D messages
50 
51     This type of msg exchanges non-EOI data bytes and commands.
52     In the output direction the remotizer implements an acceptor so
53     that the proper 3-way handshake is implemented with the
54     source. The acceptor has no delays between state transitions so
55     that it can keep up with any speed of the source.
56     In the input direction a source is implemented to handshake with
57     local acceptor(s). The FSM in the source has no delays so that it can
58     operate at whatever speed the slowest acceptor on the bus can sustain.
59     Bytes are removed from the input buffer of the stream as the local
60     acceptors acknowledge them. No input message is processed by the
61     remotizer when it's waiting for 3-way handshake with acceptors to
62     complete.
63     Byte values are expressed in positive logic (which is the opposite
64     of what's sent on the bus). To discriminate between DAB and command
65     bytes the receiver of this message has to check its state of ATN
66     signal (see R&S messages).
67     A simple infrastructure for synchronization between near and far
68     end is provided. The sender of a string of bytes can insert a
69     "checkpoint" msg in the string. This msg is turned into a
70     "checkpoint reached" msg by the receiver and sent back. This msg
71     reports to the sender that the receiver has processed data up to
72     the last checkpoint.
73     The acceptor in the remotizer sends a checkpoint in two cases: when a
74     byte with EOI is accepted and sent to the far end or when there's
75     a pause in the byte string longer than 10 ms. This is just for
76     safety, normal byte strings are always terminated by EOI by the
77     sender. The remotizer acceptor stops accepting data after sending
78     a checkpoint. It resumes when the "checkpoint reached" msg is sent
79     back. Bytes of I/F commands never use checkpointing.
80     The source handshake (SH) in the remotizer accepts data bytes from
81     the far end and puts them on the local bus. A simple heuristic is
82     implemented to recognize the situation when the far end has sent
83     more data than a local acceptor is willing to take. Whenever ATN
84     is asserted by a local device (the controller) all input bytes are
85     discarded up to the next checkpoint. The "checkpoint reached" msg
86     then reports that data has been completely/partially flushed by
87     setting its byte to 1. In normal cases byte is set to 0.
88     Usage of checkpointing is optional. The minimum the far end should
89     implement is sending back a "checkpoint reached" msg whenever a
90     checkpoint is received.
91 
92     - E messages
93 
94     This type of message carries DAB that are sourced on the bus with
95     EOI asserted. These messages have the same logic of 'D' messages
96     (see above).  These messages never carry command bytes (because
97     the condition where both ATN & EOI are asserted is used for
98     parallel polling).
99 
100     - J & K messages
101 
102     This message pair is used to probe the connection with the
103     external devices because bitbanger streams do not report in any
104     way if they are connected to outside or not. Any output byte is
105     silently discarded if the stream is not connected.
106     From the remotizer point of view the connection is up when a
107     message of any kind comes in from the outside. When there's no
108     traffic to exchange, the remotizer sends a J message every 0.5 s.
109     The remote device should reply with a K msg to every J msg it
110     sees.  Connection status is set to "down" when three consecutive J
111     msgs are sent out without being replied to.
112 
113     - P & Q messages
114 
115     The 'P' msg is used by an external device to set the response to
116     be placed on the bus when a local controller does a parallel
117     poll. Message byte is encoded in positive logic (i.e. any bit set
118     to 1 is forced to 0 on data bus during parallel poll).  The
119     response set by P msg is used during parallel polls until changed
120     by another P msg. A P msg is never sent out by the remotizer.
121     The Q msg is sent by the remotizer whenever a parallel poll is
122     performed locally. Its purpose is to solicit a P msg from the
123     external device. The external device may choose to send P msgs in
124     reply to Q msgs or to ignore them and send P msgs asynchronously.
125     Parallel poll is usually a very fast operation (a few microseconds)
126     so it's possible, due to latency, that the reply to a Q msg is
127     applied on the bus many polls later. The remotizer doesn't repeat
128     the transmission of a Q msg until a P msg is received to avoid
129     generating a lot of traffic if the local controller rapidly
130     repeats parallel polls.
131 
132     - R & S messages
133 
134     These messages are used to align the state of bus control lines
135     with the external devices. Each control line is mapped to a bit in
136     the accompanying byte, according to this table.
137 
138     | Bit | Signal |
139     |-----+--------|
140     | 0   | ATN    |
141     | 1   | IFC    |
142     | 2   | REN    |
143     | 3   | SRQ    |
144 
145     A "R" message clears (sets to 0, i.e. asserts) all signals that
146     are set to "1" in the byte, all others retain their value.
147     A "S" message sets signals to 1, i.e. de-asserts them.
148     The remotizer may respond with a "R" message if an incoming "S"
149     message is attempting to set to 1 a signal that is locally forced
150     to 0.
151 
152     - X & Y messages
153 
154     See "D" messages above. "X" message is sent to request the
155     receiver to acknowledge the reception by sending back a "Y"
156     message.  When used, the "X" message should be sent at the end of
157     a string of bytes by the sender. The reception of "Y" message
158     confirms that the receiver has seen (though not necessarily
159     accepted) the whole string.
160     The "X" -> "Y" sequence is used in the local AH for flow control:
161     once "X" is sent out, the acceptor pauses accepting bytes until a
162     "Y" is received.  For this reason the far end should always
163     implement, at the very least, the sending back of "Y" whenever "X"
164     is seen.
165     A "X" message carries no info in its byte. A "Y" message reports
166     whether the preceding byte string was entirely accepted by the
167     receiver or not (with a 00 or 01 value in the associated byte,
168     respectively).
169 
170     Limits & potential issues:
171     - There are no checks for violations of IEEE-488 protocol. It's
172       possible, for example, to put the bus in an illegal state by
173       sending a 'E' msg when the local controller is asserting ATN
174       (thus creating a phantom parallel poll).
175     - The heuristic to discard byte strings in the SH could be a bit
176       too simple and do the wrong thing in few rare cases.
177     - It's difficult to achieve accurate synchronization between the
178       local emulation time and the external time.
179 
180     TODOs/possible enhancements:
181     - Implement handling of incoming Q msgs (needed when parallel poll
182       is being performed by a remote controller)
183     - Enhancement: implement a msg for accurate time synchronization
184 
185 *********************************************************************/
186 
187 #include "emu.h"
188 #include "remote488.h"
189 
190 // Debugging
191 #include "logmacro.h"
192 #define LOG_PARSER_MASK (LOG_GENERAL << 1)
193 #define LOG_PARSER(...) LOGMASKED(LOG_PARSER_MASK, __VA_ARGS__)
194 #undef VERBOSE
195 #define VERBOSE LOG_GENERAL
196 
197 // Bit manipulation
198 namespace {
BIT_MASK(unsigned n)199 	template<typename T> constexpr T BIT_MASK(unsigned n)
200 	{
201 		return (T)1U << n;
202 	}
203 
BIT_CLR(T & w,unsigned n)204 	template<typename T> void BIT_CLR(T& w , unsigned n)
205 	{
206 		w &= ~BIT_MASK<T>(n);
207 	}
208 
BIT_SET(T & w,unsigned n)209 	template<typename T> void BIT_SET(T& w , unsigned n)
210 	{
211 		w |= BIT_MASK<T>(n);
212 	}
213 
COPY_BIT(bool bit,T & w,unsigned n)214 	template<typename T> void COPY_BIT(bool bit , T& w , unsigned n)
215 	{
216 		if (bit) {
217 			BIT_SET(w , n);
218 		} else {
219 			BIT_CLR(w , n);
220 		}
221 	}
222 }
223 
224 // Message types
225 constexpr char MSG_SIGNAL_CLEAR  = 'R'; // I/O: Clear signal(s)
226 constexpr char MSG_SIGNAL_SET    = 'S'; // I/O: Set signal(s)
227 constexpr char MSG_DATA_BYTE     = 'D'; // I/O: Cmd/data byte (no EOI)
228 constexpr char MSG_END_BYTE      = 'E'; // I/O: Data byte (with EOI)
229 constexpr char MSG_PP_DATA       = 'P'; // I:   Parallel poll data
230 constexpr char MSG_PP_REQUEST    = 'Q'; // O:   Request PP data
231 constexpr char MSG_ECHO_REQ      = 'J'; // O:   Heartbeat msg: echo request
232 constexpr char MSG_ECHO_REPLY    = 'K'; // I:   Heartbeat msg: echo reply
233 constexpr char MSG_CHECKPOINT    = 'X'; // I/O: Checkpoint in byte stream
234 constexpr char MSG_CP_REACHED    = 'Y'; // I/O: Checkpoint reached
235 
236 // Timings
237 constexpr unsigned POLL_PERIOD_US   = 20;   // Poll period (µs)
238 constexpr unsigned HEARTBEAT_MS     = 500;  // Heartbeat ping period (ms)
239 constexpr unsigned MAX_MISSED_HB    = 3;    // Missed heartbeats to declare the connection dead
240 constexpr unsigned AH_TO_MS         = 10;   // Timeout in AH to report a byte string terminated (ms)
241 
242 // device type definition
243 DEFINE_DEVICE_TYPE(REMOTE488, remote488_device, "remote488", "IEEE-488 Remotizer")
244 
remote488_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)245 remote488_device::remote488_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
246 	device_t(mconfig , REMOTE488 , tag , owner , clock),
247 	device_ieee488_interface(mconfig , *this),
248 	m_stream(*this , "stream")
249 {
250 }
251 
device_add_mconfig(machine_config & config)252 void remote488_device::device_add_mconfig(machine_config &config)
253 {
254 	BITBANGER(config, m_stream, 0);
255 }
256 
ieee488_eoi(int state)257 void remote488_device::ieee488_eoi(int state)
258 {
259 	update_pp();
260 }
261 
ieee488_dav(int state)262 void remote488_device::ieee488_dav(int state)
263 {
264 	update_ah_fsm();
265 }
266 
ieee488_nrfd(int state)267 void remote488_device::ieee488_nrfd(int state)
268 {
269 	update_sh_fsm();
270 }
271 
ieee488_ndac(int state)272 void remote488_device::ieee488_ndac(int state)
273 {
274 	update_sh_fsm();
275 }
276 
ieee488_ifc(int state)277 void remote488_device::ieee488_ifc(int state)
278 {
279 	update_signal(SIGNAL_IFC_BIT , state);
280 	if (!state) {
281 		LOG("IFC\n");
282 		flush_data();
283 		bus_reset();
284 	}
285 }
286 
ieee488_srq(int state)287 void remote488_device::ieee488_srq(int state)
288 {
289 	update_signal(SIGNAL_SRQ_BIT , state);
290 }
291 
ieee488_atn(int state)292 void remote488_device::ieee488_atn(int state)
293 {
294 	update_signal(SIGNAL_ATN_BIT , state);
295 	update_sh_fsm();
296 	update_ah_fsm();
297 	update_pp();
298 }
299 
ieee488_ren(int state)300 void remote488_device::ieee488_ren(int state)
301 {
302 	update_signal(SIGNAL_REN_BIT , state);
303 }
304 
device_start()305 void remote488_device::device_start()
306 {
307 	m_poll_timer = timer_alloc(TMR_ID_POLL);
308 	m_hb_timer = timer_alloc(TMR_ID_HEARTBEAT);
309 	m_ah_timer = timer_alloc(TMR_ID_AH);
310 }
311 
device_reset()312 void remote488_device::device_reset()
313 {
314 	m_no_propagation = true;
315 	m_in_signals = 0xff;
316 	m_bus->atn_w(this , 1);
317 	m_bus->eoi_w(this , 1);
318 	m_bus->ifc_w(this , 1);
319 	m_bus->ren_w(this , 1);
320 	m_bus->srq_w(this , 1);
321 	m_out_signals = 0xff;
322 	m_no_propagation = false;
323 
324 	// Fake disconnection
325 	m_connected = true;
326 	set_connection(false);
327 
328 	m_ibf = false;
329 	m_flush_bytes = false;
330 	m_waiting_cp = false;
331 	bus_reset();
332 }
333 
bus_reset()334 void remote488_device::bus_reset()
335 {
336 	m_sh_state = REM_SH_SIDS;
337 	m_ah_state = REM_AH_ACRS;
338 	m_rx_state = REM_RX_WAIT_CH;
339 	m_poll_timer->adjust(attotime::from_usec(POLL_PERIOD_US) , 0 , attotime::from_usec(POLL_PERIOD_US));
340 	m_pp_data = 0;
341 	m_pp_requested = false;
342 	update_ah_fsm();
343 	update_sh_fsm();
344 	update_pp();
345 }
346 
process_input_msgs()347 void remote488_device::process_input_msgs()
348 {
349 	uint8_t data;
350 	char msg_ch;
351 	while ((msg_ch = recv_update(data)) != 0) {
352 		set_connection(true);
353 		LOG("%.6f Rx %c %02x\n" , machine().time().as_double() , msg_ch , data);
354 
355 		switch (msg_ch) {
356 		case MSG_SIGNAL_CLEAR:
357 			update_signals_from_rem(0 , data);
358 			break;
359 
360 		case MSG_SIGNAL_SET:
361 			update_signals_from_rem(data , 0);
362 			break;
363 
364 		case MSG_DATA_BYTE:
365 		case MSG_END_BYTE:
366 			if (m_flush_bytes) {
367 				LOG("Flushed\n");
368 				m_poll_timer->adjust(attotime::zero);
369 			} else {
370 				m_poll_timer->reset();
371 				recvd_data_byte(data , msg_ch == MSG_END_BYTE);
372 			}
373 			return;
374 
375 		case MSG_PP_DATA:
376 			m_pp_data = data;
377 			m_pp_requested = false;
378 			update_pp_dio();
379 			break;
380 
381 		case MSG_ECHO_REQ:
382 			send_update(MSG_ECHO_REPLY , 0);
383 			break;
384 
385 		case MSG_CHECKPOINT:
386 			send_update(MSG_CP_REACHED , m_flush_bytes);
387 			m_flush_bytes = false;
388 			break;
389 
390 		case MSG_CP_REACHED:
391 			if (m_waiting_cp) {
392 				m_waiting_cp = false;
393 				update_ah_fsm();
394 			}
395 			break;
396 
397 		default:
398 			break;
399 		}
400 	}
401 	if (!m_poll_timer->enabled()) {
402 		m_poll_timer->adjust(attotime::from_usec(POLL_PERIOD_US) , 0 , attotime::from_usec(POLL_PERIOD_US));
403 	}
404 }
405 
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)406 void remote488_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
407 {
408 	switch (id) {
409 	case TMR_ID_POLL:
410 		process_input_msgs();
411 		break;
412 
413 	case TMR_ID_HEARTBEAT:
414 		if (m_connected && m_connect_cnt && --m_connect_cnt == 0) {
415 			set_connection(false);
416 		}
417 		send_update(MSG_ECHO_REQ , 0);
418 		break;
419 
420 	case TMR_ID_AH:
421 		if (!m_waiting_cp) {
422 			LOG("CP T/O\n");
423 			ah_checkpoint();
424 		}
425 		break;
426 
427 	default:
428 		break;
429 	}
430 }
431 
set_connection(bool state)432 void remote488_device::set_connection(bool state)
433 {
434 	if (state) {
435 		if (!m_connected) {
436 			// Just connected
437 			m_connected = true;
438 			LOG("Connected!\n");
439 			// Align signal state on both sides
440 			uint8_t tmp = m_out_signals & ((1 << SIGNAL_COUNT) - 1);
441 			if (tmp) {
442 				send_update(MSG_SIGNAL_SET , tmp);
443 			}
444 			tmp = ~m_out_signals & ((1 << SIGNAL_COUNT) - 1);
445 			if (tmp) {
446 				send_update(MSG_SIGNAL_CLEAR , tmp);
447 			}
448 		}
449 		m_hb_timer->adjust(attotime::from_msec(HEARTBEAT_MS) , 0 , attotime::from_msec(HEARTBEAT_MS));
450 		m_connect_cnt = MAX_MISSED_HB;
451 	} else {
452 		if (m_connected) {
453 			// Disconnected
454 			m_connected = false;
455 			LOG("Connection lost!\n");
456 			update_ah_fsm();
457 			m_hb_timer->adjust(attotime::from_msec(HEARTBEAT_MS) , 0 , attotime::from_msec(HEARTBEAT_MS));
458 		}
459 	}
460 }
461 
recvd_data_byte(uint8_t data,bool eoi)462 void remote488_device::recvd_data_byte(uint8_t data , bool eoi)
463 {
464 	m_ib = data;
465 	m_ib_eoi = eoi;
466 	m_ibf = true;
467 	if (is_local_atn_active()) {
468 		flush_data();
469 	}
470 	update_sh_fsm();
471 	update_ah_fsm();
472 }
473 
flush_data()474 void remote488_device::flush_data()
475 {
476 	if (m_ibf) {
477 		LOG("Flushing enabled\n");
478 		m_flush_bytes = true;
479 		m_ibf = false;
480 		m_poll_timer->adjust(attotime::zero);
481 	}
482 }
483 
update_signals_from_rem(uint8_t to_set,uint8_t to_clear)484 void remote488_device::update_signals_from_rem(uint8_t to_set , uint8_t to_clear)
485 {
486 	uint8_t diff = m_in_signals;
487 	m_in_signals |= to_set;
488 	m_in_signals &= ~to_clear;
489 	diff ^= m_in_signals;
490 
491 	//LOG("REM SIG %02x %02x\n" , m_in_signals , diff);
492 	m_no_propagation = true;
493 
494 	if (BIT(diff , SIGNAL_ATN_BIT)) {
495 		m_bus->atn_w(this , BIT(m_in_signals , SIGNAL_ATN_BIT));
496 	}
497 	if (BIT(diff , SIGNAL_IFC_BIT)) {
498 		m_bus->ifc_w(this , BIT(m_in_signals , SIGNAL_IFC_BIT));
499 	}
500 	if (BIT(diff , SIGNAL_REN_BIT)) {
501 		m_bus->ren_w(this , BIT(m_in_signals , SIGNAL_REN_BIT));
502 	}
503 	if (BIT(diff , SIGNAL_SRQ_BIT)) {
504 		m_bus->srq_w(this , BIT(m_in_signals , SIGNAL_SRQ_BIT));
505 	}
506 
507 	m_no_propagation = false;
508 }
509 
update_signal(signal_bit bit,int state)510 void remote488_device::update_signal(signal_bit bit , int state)
511 {
512 	if (!m_no_propagation) {
513 		uint8_t tmp = m_out_signals;
514 		COPY_BIT(state , tmp , bit);
515 		update_state(tmp);
516 	}
517 }
518 
update_state(uint8_t new_signals)519 void remote488_device::update_state(uint8_t new_signals)
520 {
521 	uint8_t to_set = new_signals & ~m_out_signals;
522 	uint8_t to_clear = ~new_signals & m_out_signals;
523 
524 	m_out_signals = new_signals;
525 
526 	if (is_local_atn_active()) {
527 		flush_data();
528 	}
529 
530 	if (to_set) {
531 		send_update(MSG_SIGNAL_SET , to_set);
532 	}
533 	if (to_clear) {
534 		send_update(MSG_SIGNAL_CLEAR , to_clear);
535 	}
536 }
537 
send_update(char type,uint8_t data)538 void remote488_device::send_update(char type , uint8_t data)
539 {
540 	std::string buff = util::string_format("%c:%02x\n" , type , data);
541 	LOG("%.6f %s" , machine().time().as_double() , buff);
542 	for (char c : buff) {
543 		m_stream->output(c);
544 	}
545 }
546 
a2hex(char c,uint8_t & out)547 bool remote488_device::a2hex(char c , uint8_t& out)
548 {
549 	if (c >= '0' && c <= '9') {
550 		out = c - '0';
551 		return true;
552 	} else if (c >= 'a' && c <= 'f') {
553 		out = c - 'a' + 10;
554 		return true;
555 	} else if (c >= 'A' && c <= 'F') {
556 		out = c - 'A' + 10;
557 		return true;
558 	} else {
559 		return false;
560 	}
561 }
562 
is_msg_type(char c)563 bool remote488_device::is_msg_type(char c)
564 {
565 	// Recognize type of input messages
566 	return c == MSG_SIGNAL_CLEAR ||
567 		c == MSG_SIGNAL_SET ||
568 		c == MSG_DATA_BYTE ||
569 		c == MSG_END_BYTE ||
570 		c == MSG_PP_DATA ||
571 		c == MSG_ECHO_REPLY ||
572 		c == MSG_CHECKPOINT ||
573 		c == MSG_CP_REACHED;
574 }
575 
is_terminator(char c)576 bool remote488_device::is_terminator(char c)
577 {
578 	// Match message terminator characters
579 	return c == ',' ||
580 		c == ';';
581 }
582 
is_space(char c)583 bool remote488_device::is_space(char c)
584 {
585 	// Match whitespace characters
586 	return c == ' ' ||
587 		c == '\t' ||
588 		c == '\r' ||
589 		c == '\n';
590 }
591 
recv_update(uint8_t & data)592 char remote488_device::recv_update(uint8_t& data)
593 {
594 	char c;
595 	unsigned i;
596 
597 	// Do not iterate too much..
598 	for (i = 0; i < 8 && m_stream->input(&c , 1); i++) {
599 		int prev_state = m_rx_state;
600 		switch (m_rx_state) {
601 		case REM_RX_WAIT_CH:
602 			if (is_msg_type(c)) {
603 				m_rx_ch = c;
604 				m_rx_state = REM_RX_WAIT_COLON;
605 			} else if (!is_space(c)) {
606 				m_rx_state = REM_RX_WAIT_WS;
607 			}
608 			break;
609 
610 		case REM_RX_WAIT_COLON:
611 			if (c == ':') {
612 				m_rx_state = REM_RX_WAIT_1ST_HEX;
613 			} else {
614 				m_rx_state = REM_RX_WAIT_WS;
615 			}
616 			break;
617 
618 		case REM_RX_WAIT_1ST_HEX:
619 			if (a2hex(c , m_rx_data)) {
620 				m_rx_state = REM_RX_WAIT_2ND_HEX;
621 			} else {
622 				m_rx_state = REM_RX_WAIT_WS;
623 			}
624 			break;
625 
626 		case REM_RX_WAIT_2ND_HEX:
627 			{
628 				uint8_t tmp;
629 				if (a2hex(c , tmp)) {
630 					m_rx_data = (m_rx_data << 4) | tmp;
631 					m_rx_state = REM_RX_WAIT_SEP;
632 				} else {
633 					m_rx_state = REM_RX_WAIT_WS;
634 				}
635 			}
636 			break;
637 
638 		case REM_RX_WAIT_SEP:
639 			if (is_terminator(c) || is_space(c)) {
640 				m_rx_state = REM_RX_WAIT_CH;
641 				LOG_PARSER("PARSE %02x %d->%d\n" , c , prev_state , m_rx_state);
642 				data = m_rx_data;
643 				return m_rx_ch;
644 			} else {
645 				m_rx_state = REM_RX_WAIT_WS;
646 			}
647 			break;
648 
649 		case REM_RX_WAIT_WS:
650 			if (is_terminator(c) || is_space(c)) {
651 				m_rx_state = REM_RX_WAIT_CH;
652 			}
653 			break;
654 
655 		default:
656 			m_rx_state = REM_RX_WAIT_CH;
657 			break;
658 		}
659 		LOG_PARSER("PARSE %02x %d->%d\n" , c , prev_state , m_rx_state);
660 	}
661 	return 0;
662 }
663 
is_local_atn_active() const664 bool remote488_device::is_local_atn_active() const
665 {
666 	return !BIT(m_out_signals , SIGNAL_ATN_BIT) && BIT(m_in_signals , SIGNAL_ATN_BIT);
667 }
668 
ah_checkpoint()669 void remote488_device::ah_checkpoint()
670 {
671 	m_waiting_cp = true;
672 	m_ah_timer->reset();
673 	send_update(MSG_CHECKPOINT , 0);
674 }
675 
update_ah_fsm()676 void remote488_device::update_ah_fsm()
677 {
678 	bool changed = true;
679 
680 	while (changed) {
681 		//LOG("AH %d DAV %d\n" , m_ah_state , m_bus->dav_r());
682 		int prev_state = m_ah_state;
683 
684 		if (m_sh_state != REM_SH_SIDS || !m_connected) {
685 			m_ah_state = REM_AH_AIDS;
686 		} else {
687 			switch (m_ah_state) {
688 			case REM_AH_AIDS:
689 				m_ah_state = REM_AH_ACRS;
690 				break;
691 
692 			case REM_AH_ACRS:
693 				if (!m_bus->dav_r()) {
694 					m_ah_state = REM_AH_ACDS;
695 				}
696 				break;
697 
698 			case REM_AH_ACDS:
699 				if (m_bus->dav_r()) {
700 					m_ah_state = REM_AH_ACRS;
701 				} else if (!m_waiting_cp) {
702 					uint8_t dio = ~m_bus->dio_r();
703 
704 					if (!m_bus->eoi_r()) {
705 						send_update(MSG_END_BYTE , dio);
706 						ah_checkpoint();
707 					} else {
708 						send_update(MSG_DATA_BYTE , dio);
709 						if (!BIT(m_out_signals , SIGNAL_ATN_BIT)) {
710 							// I/F commands have no checkpoint
711 							m_ah_timer->reset();
712 						} else {
713 							m_ah_timer->adjust(attotime::from_msec(AH_TO_MS));
714 						}
715 					}
716 					m_ah_state = REM_AH_AWNS;
717 				}
718 				break;
719 
720 			case REM_AH_AWNS:
721 				if (m_bus->dav_r()) {
722 					m_ah_state = REM_AH_ACRS;
723 				}
724 				break;
725 
726 			default:
727 				m_ah_state = REM_AH_ACRS;
728 				break;
729 			}
730 		}
731 		changed = prev_state != m_ah_state;
732 
733 		m_bus->ndac_w(this , m_ah_state == REM_AH_AIDS || m_ah_state == REM_AH_AWNS);
734 		m_bus->nrfd_w(this , m_ah_state == REM_AH_AIDS || m_ah_state == REM_AH_ACRS);
735 	}
736 }
737 
update_sh_fsm()738 void remote488_device::update_sh_fsm()
739 {
740 	bool changed = true;
741 
742 	while (changed) {
743 		int prev_state = m_sh_state;
744 		//LOG("SH %d LATN %d NRFD %d NDAC %d\n" , m_sh_state , is_local_atn_active() , m_bus->nrfd_r() , m_bus->ndac_r());
745 
746 		if (is_local_atn_active() || !m_ibf) {
747 			// Reset condition
748 			m_sh_state = REM_SH_SIDS;
749 		} else {
750 			switch (m_sh_state) {
751 			case REM_SH_SIDS:
752 				m_sh_state = REM_SH_SDYS;
753 				break;
754 
755 			case REM_SH_SDYS:
756 				if (m_bus->nrfd_r()) {
757 					m_sh_state = REM_SH_STRS;
758 				}
759 				break;
760 
761 			case REM_SH_STRS:
762 				if (m_bus->ndac_r()) {
763 					m_sh_state = REM_SH_SIDS;
764 					LOG("Sourced %02x\n" , m_ib);
765 					m_ibf = false;
766 					// Schedule an immediate poll for incoming messages
767 					// This allows sourcing string of bytes on the bus at the highest possible speed
768 					m_poll_timer->adjust(attotime::zero);
769 				}
770 				break;
771 
772 			default:
773 				m_sh_state = REM_SH_SIDS;
774 				break;
775 			}
776 		}
777 		changed = prev_state != m_sh_state;
778 
779 		if (m_sh_state == REM_SH_SDYS || m_sh_state == REM_SH_STRS) {
780 			m_bus->eoi_w(this , !m_ib_eoi);
781 			m_sh_dio = ~m_ib;
782 		} else {
783 			m_bus->eoi_w(this , 1);
784 			m_sh_dio = 0xff;
785 		}
786 		update_dio();
787 		m_bus->dav_w(this , m_sh_state == REM_SH_SIDS || m_sh_state == REM_SH_SDYS);
788 	}
789 }
790 
is_local_pp_active() const791 bool remote488_device::is_local_pp_active() const
792 {
793 	return is_local_atn_active() && !m_bus->eoi_r();
794 }
795 
update_pp()796 void remote488_device::update_pp()
797 {
798 	if (is_local_pp_active() && m_connected && !m_pp_requested) {
799 		send_update(MSG_PP_REQUEST , 0);
800 		m_pp_requested = true;
801 	}
802 	update_pp_dio();
803 }
804 
update_pp_dio()805 void remote488_device::update_pp_dio()
806 {
807 	if (is_local_pp_active()) {
808 		LOG("PP %02x\n" , m_pp_data);
809 		m_pp_dio = ~m_pp_data;
810 	} else {
811 		m_pp_dio = 0xff;
812 	}
813 	update_dio();
814 }
815 
update_dio()816 void remote488_device::update_dio()
817 {
818 	m_bus->dio_w(this , m_pp_dio & m_sh_dio);
819 }
820