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