summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices
diff options
context:
space:
mode:
author R. Belmont <rb6502@users.noreply.github.com>2018-05-23 20:25:42 -0400
committer GitHub <noreply@github.com>2018-05-23 20:25:42 -0400
commitfde1143a22ea828a5b97b6e3f26cf2b6d6773d5d (patch)
tree5da04a53e2ff432d670cc90ba930724ade2ac846 /src/devices
parent1a51fb63b9577914241e46e7fd3558037d204644 (diff)
parentd55e7f910facd153357cb52065927b6b78377084 (diff)
Merge pull request #3598 from fulivi/rem488_dev4
remote488: checkpointing added
Diffstat (limited to 'src/devices')
-rw-r--r--src/devices/bus/ieee488/remote488.cpp156
-rw-r--r--src/devices/bus/ieee488/remote488.h7
2 files changed, 120 insertions, 43 deletions
diff --git a/src/devices/bus/ieee488/remote488.cpp b/src/devices/bus/ieee488/remote488.cpp
index bc4c8623d0e..1c14266796c 100644
--- a/src/devices/bus/ieee488/remote488.cpp
+++ b/src/devices/bus/ieee488/remote488.cpp
@@ -43,6 +43,8 @@
| Q | O | No | Request 'P' msg |
| R | I/O | Yes | Set bus control signals to 0 |
| S | I/O | Yes | Set bus control signals to 1 |
+ | X | I/O | No | Checkpoint in byte string |
+ | Y | I/O | Yes | Checkpoint reached |
- D messages
@@ -50,24 +52,42 @@
In the output direction the remotizer implements an acceptor so
that the proper 3-way handshake is implemented with the
source. The acceptor has no delays between state transitions so
- that it can keep up with any speed of the source. The acceptor
- doesn't wait for any acknowledgement from external receiver. The
- buffering in the output stream must have enough space to hold
- bursts of data bytes before they are processed by the external
- receiver.
+ that it can keep up with any speed of the source.
In the input direction a source is implemented to handshake with
local acceptor(s). The FSM in the source has no delays so that it can
operate at whatever speed the slowest acceptor on the bus can sustain.
Bytes are removed from the input buffer of the stream as the local
acceptors acknowledge them. No input message is processed by the
remotizer when it's waiting for 3-way handshake with acceptors to
- complete. If a local controller asserts the ATN signal to regain
- control of the bus when there are input bytes waiting, the bytes
- are discarded.
+ complete.
Byte values are expressed in positive logic (which is the opposite
of what's sent on the bus). To discriminate between DAB and command
bytes the receiver of this message has to check its state of ATN
signal (see R&S messages).
+ A simple infrastructure for synchronization between near and far
+ end is provided. The sender of a string of bytes can insert a
+ "checkpoint" msg in the string. This msg is turned into a
+ "checkpoint reached" msg by the receiver and sent back. This msg
+ reports to the sender that the receiver has processed data up to
+ the last checkpoint.
+ The acceptor in the remotizer sends a checkpoint in two cases: when a
+ byte with EOI is accepted and sent to the far end or when there's
+ a pause in the byte string longer than 10 ms. This is just for
+ safety, normal byte strings are always terminated by EOI by the
+ sender. The remotizer acceptor stops accepting data after sending
+ a checkpoint. It resumes when the "checkpoint reached" msg is sent
+ back. Bytes of I/F commands never use checkpointing.
+ The source handshake (SH) in the remotizer accepts data bytes from
+ the far end and puts them on the local bus. A simple heuristic is
+ implemented to recognize the situation when the far end has sent
+ more data than a local acceptor is willing to take. Whenever ATN
+ is asserted by a local device (the controller) all input bytes are
+ discarded up to the next checkpoint. The "checkpoint reached" msg
+ then reports that data has been completely/partially flushed by
+ setting its byte to 1. In normal cases byte is set to 0.
+ Usage of checkpointing is optional. The minimum the far end should
+ implement is sending back a "checkpoint reached" msg whenever a
+ checkpoint is received.
- E messages
@@ -129,16 +149,31 @@
message is attempting to set to 1 a signal that is locally forced
to 0.
+ - X & Y messages
+
+ See "D" messages above. "X" message is sent to request the
+ receiver to acknowledge the reception by sending back a "Y"
+ message. When used, the "X" message should be sent at the end of
+ a string of bytes by the sender. The reception of "Y" message
+ confirms that the receiver has seen (though not necessarily
+ accepted) the whole string.
+ The "X" -> "Y" sequence is used in the local AH for flow control:
+ once "X" is sent out, the acceptor pauses accepting bytes until a
+ "Y" is received. For this reason the far end should always
+ implement, at the very least, the sending back of "Y" whenever "X"
+ is seen.
+ A "X" message carries no info in its byte. A "Y" message reports
+ whether the preceding byte string was entirely accepted by the
+ receiver or not (with a 00 or 01 value in the associated byte,
+ respectively).
+
Limits & potential issues:
- There are no checks for violations of IEEE-488 protocol. It's
possible, for example, to put the bus in an illegal state by
sending a 'E' msg when the local controller is asserting ATN
(thus creating a phantom parallel poll).
- - The unacknowledged nature of D/E msgs may cause problems if
- the sender of the msgs must be synchronized byte-for-byte with
- the receiver. There is no direct way, for example, for the
- sender of a string of bytes to know how many have been accepted
- by the receiver.
+ - The heuristic to discard byte strings in the SH could be a bit
+ too simple and do the wrong thing in few rare cases.
- It's difficult to achieve accurate synchronization between the
local emulation time and the external time.
@@ -146,10 +181,6 @@
- Implement handling of incoming Q msgs (needed when parallel poll
is being performed by a remote controller)
- Enhancement: implement a msg for accurate time synchronization
- - Enhancement: implement some form of sliding window acknowledgement
- for cases when sender has to know how many bytes have been
- processed by the receiver. The HP "Amigo" protocol I used for
- my experiments has no such need.
*********************************************************************/
@@ -199,11 +230,14 @@ constexpr char MSG_PP_DATA = 'P'; // I: Parallel poll data
constexpr char MSG_PP_REQUEST = 'Q'; // O: Request PP data
constexpr char MSG_ECHO_REQ = 'J'; // O: Heartbeat msg: echo request
constexpr char MSG_ECHO_REPLY = 'K'; // I: Heartbeat msg: echo reply
+constexpr char MSG_CHECKPOINT = 'X'; // I/O: Checkpoint in byte stream
+constexpr char MSG_CP_REACHED = 'Y'; // I/O: Checkpoint reached
// Timings
constexpr unsigned POLL_PERIOD_US = 20; // Poll period (µs)
constexpr unsigned HEARTBEAT_MS = 500; // Heartbeat ping period (ms)
constexpr unsigned MAX_MISSED_HB = 3; // Missed heartbeats to declare the connection dead
+constexpr unsigned AH_TO_MS = 10; // Timeout in AH to report a byte string terminated (ms)
// device type definition
DEFINE_DEVICE_TYPE(REMOTE488, remote488_device, "remote488", "IEEE-488 Remotizer")
@@ -244,6 +278,7 @@ void remote488_device::ieee488_ifc(int state)
update_signal(SIGNAL_IFC_BIT , state);
if (!state) {
LOG("IFC\n");
+ flush_data();
bus_reset();
}
}
@@ -257,6 +292,7 @@ void remote488_device::ieee488_atn(int state)
{
update_signal(SIGNAL_ATN_BIT , state);
update_sh_fsm();
+ update_ah_fsm();
update_pp();
}
@@ -269,6 +305,7 @@ void remote488_device::device_start()
{
m_poll_timer = timer_alloc(TMR_ID_POLL);
m_hb_timer = timer_alloc(TMR_ID_HEARTBEAT);
+ m_ah_timer = timer_alloc(TMR_ID_AH);
}
void remote488_device::device_reset()
@@ -287,6 +324,9 @@ void remote488_device::device_reset()
m_connected = true;
set_connection(false);
+ m_ibf = false;
+ m_flush_bytes = false;
+ m_waiting_cp = false;
bus_reset();
}
@@ -295,8 +335,6 @@ void remote488_device::bus_reset()
m_sh_state = REM_SH_SIDS;
m_ah_state = REM_AH_ACRS;
m_rx_state = REM_RX_WAIT_CH;
- m_ibf = false;
- m_flush_bytes = false;
m_poll_timer->adjust(attotime::from_usec(POLL_PERIOD_US) , 0 , attotime::from_usec(POLL_PERIOD_US));
m_pp_data = 0;
m_pp_requested = false;
@@ -315,38 +353,25 @@ void remote488_device::process_input_msgs()
switch (msg_ch) {
case MSG_SIGNAL_CLEAR:
- m_flush_bytes = false;
update_signals_from_rem(0 , data);
break;
case MSG_SIGNAL_SET:
- m_flush_bytes = false;
update_signals_from_rem(data , 0);
break;
case MSG_DATA_BYTE:
- if (m_flush_bytes) {
- LOG("Flushed\n");
- } else {
- m_poll_timer->reset();
- recvd_data_byte(data , false);
- return;
- }
- break;
-
case MSG_END_BYTE:
if (m_flush_bytes) {
LOG("Flushed\n");
- m_flush_bytes = false;
+ m_poll_timer->adjust(attotime::zero);
} else {
m_poll_timer->reset();
- recvd_data_byte(data , true);
- return;
+ recvd_data_byte(data , msg_ch == MSG_END_BYTE);
}
- break;
+ return;
case MSG_PP_DATA:
- m_flush_bytes = false;
m_pp_data = data;
m_pp_requested = false;
update_pp_dio();
@@ -356,6 +381,18 @@ void remote488_device::process_input_msgs()
send_update(MSG_ECHO_REPLY , 0);
break;
+ case MSG_CHECKPOINT:
+ send_update(MSG_CP_REACHED , m_flush_bytes);
+ m_flush_bytes = false;
+ break;
+
+ case MSG_CP_REACHED:
+ if (m_waiting_cp) {
+ m_waiting_cp = false;
+ update_ah_fsm();
+ }
+ break;
+
default:
break;
}
@@ -379,6 +416,13 @@ void remote488_device::device_timer(emu_timer &timer, device_timer_id id, int pa
send_update(MSG_ECHO_REQ , 0);
break;
+ case TMR_ID_AH:
+ if (!m_waiting_cp) {
+ LOG("CP T/O\n");
+ ah_checkpoint();
+ }
+ break;
+
default:
break;
}
@@ -419,10 +463,23 @@ void remote488_device::recvd_data_byte(uint8_t data , bool eoi)
m_ib = data;
m_ib_eoi = eoi;
m_ibf = true;
+ if (is_local_atn_active()) {
+ flush_data();
+ }
update_sh_fsm();
update_ah_fsm();
}
+void remote488_device::flush_data()
+{
+ if (m_ibf) {
+ LOG("Flushing enabled\n");
+ m_flush_bytes = true;
+ m_ibf = false;
+ m_poll_timer->adjust(attotime::zero);
+ }
+}
+
void remote488_device::update_signals_from_rem(uint8_t to_set , uint8_t to_clear)
{
uint8_t diff = m_in_signals;
@@ -473,11 +530,8 @@ void remote488_device::update_state(uint8_t new_signals)
m_out_signals = new_signals;
- if (is_local_atn_active() && m_ibf) {
- LOG("Flushing enabled\n");
- m_flush_bytes = true;
- m_ibf = false;
- m_poll_timer->adjust(attotime::zero);
+ if (is_local_atn_active()) {
+ flush_data();
}
if (to_set) {
@@ -521,7 +575,9 @@ bool remote488_device::is_msg_type(char c)
c == MSG_DATA_BYTE ||
c == MSG_END_BYTE ||
c == MSG_PP_DATA ||
- c == MSG_ECHO_REPLY;
+ c == MSG_ECHO_REPLY ||
+ c == MSG_CHECKPOINT ||
+ c == MSG_CP_REACHED;
}
bool remote488_device::is_terminator(char c)
@@ -617,6 +673,13 @@ bool remote488_device::is_local_atn_active() const
return !BIT(m_out_signals , SIGNAL_ATN_BIT) && BIT(m_in_signals , SIGNAL_ATN_BIT);
}
+void remote488_device::ah_checkpoint()
+{
+ m_waiting_cp = true;
+ m_ah_timer->reset();
+ send_update(MSG_CHECKPOINT , 0);
+}
+
void remote488_device::update_ah_fsm()
{
bool changed = true;
@@ -640,13 +703,22 @@ void remote488_device::update_ah_fsm()
break;
case REM_AH_ACDS:
- {
+ if (m_bus->dav_r()) {
+ m_ah_state = REM_AH_ACRS;
+ } else if (!m_waiting_cp) {
uint8_t dio = ~m_bus->dio_r();
if (!m_bus->eoi_r()) {
send_update(MSG_END_BYTE , dio);
+ ah_checkpoint();
} else {
send_update(MSG_DATA_BYTE , dio);
+ if (!BIT(m_out_signals , SIGNAL_ATN_BIT)) {
+ // I/F commands have no checkpoint
+ m_ah_timer->reset();
+ } else {
+ m_ah_timer->adjust(attotime::from_msec(AH_TO_MS));
+ }
}
m_ah_state = REM_AH_AWNS;
}
diff --git a/src/devices/bus/ieee488/remote488.h b/src/devices/bus/ieee488/remote488.h
index fb5d267e901..312fc260f42 100644
--- a/src/devices/bus/ieee488/remote488.h
+++ b/src/devices/bus/ieee488/remote488.h
@@ -34,7 +34,8 @@ public:
// Timers
enum {
TMR_ID_POLL,
- TMR_ID_HEARTBEAT
+ TMR_ID_HEARTBEAT,
+ TMR_ID_AH
};
protected:
@@ -93,17 +94,20 @@ private:
bool m_ib_eoi;
emu_timer *m_poll_timer;
emu_timer *m_hb_timer;
+ emu_timer *m_ah_timer;
unsigned m_connect_cnt;
bool m_connected;
uint8_t m_pp_data;
bool m_pp_requested;
uint8_t m_pp_dio;
uint8_t m_sh_dio;
+ bool m_waiting_cp;
void bus_reset();
void process_input_msgs();
void set_connection(bool state);
void recvd_data_byte(uint8_t data , bool eoi);
+ void flush_data();
void update_signals_from_rem(uint8_t to_set , uint8_t to_clear);
void update_signal(signal_bit bit , int state);
void update_state(uint8_t new_signals);
@@ -114,6 +118,7 @@ private:
static bool is_space(char c);
char recv_update(uint8_t& data);
bool is_local_atn_active() const;
+ void ah_checkpoint();
void update_ah_fsm();
void update_sh_fsm();
bool is_local_pp_active() const;