HP2100: Fix MPX behaviors from Dave Bryan
This commit is contained in:
parent
a59cd3bd3b
commit
592deb8f03
3 changed files with 123 additions and 24 deletions
|
@ -1,6 +1,6 @@
|
||||||
HP 2100 SIMULATOR BUG FIX WRITEUPS
|
HP 2100 SIMULATOR BUG FIX WRITEUPS
|
||||||
==================================
|
==================================
|
||||||
Last update: 2016-03-04
|
Last update: 2016-07-28
|
||||||
|
|
||||||
|
|
||||||
1. PROBLEM: Booting from magnetic tape reports "HALT instruction, P: 77756
|
1. PROBLEM: Booting from magnetic tape reports "HALT instruction, P: 77756
|
||||||
|
@ -6936,3 +6936,83 @@
|
||||||
"hp2100_ds.c" and "hp2100_di_da.c" to use the new include file name.
|
"hp2100_ds.c" and "hp2100_di_da.c" to use the new include file name.
|
||||||
|
|
||||||
STATUS: Fixed in version 4.0-0.
|
STATUS: Fixed in version 4.0-0.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
271. PROBLEM: The MPX device incorrectly reports a currently filling read
|
||||||
|
buffer as complete.
|
||||||
|
|
||||||
|
VERSION: 4.0-0
|
||||||
|
|
||||||
|
OBSERVATION: Using Kermit to upload a file to RTE and specifying a packet
|
||||||
|
size larger than 254 bytes fails with transmission errors. Uploads with
|
||||||
|
packet sizes under 254 bytes work properly.
|
||||||
|
|
||||||
|
CAUSE: The 12792 multiplexer provides two 254-byte receive buffers per
|
||||||
|
line. A single reception larger than 254 bytes returns "partial buffer"
|
||||||
|
status when the initial read terminates because of the buffer full
|
||||||
|
condition. This informs RTE that the reception is incomplete and that it
|
||||||
|
should issue additional reads to obtain the remainder of the data.
|
||||||
|
Complete reception is indicated by the final read not returning "partial
|
||||||
|
buffer" status.
|
||||||
|
|
||||||
|
When Kermit sends a large packet, the multiplexer fills the first 254-byte
|
||||||
|
buffer, terminating on the buffer-full condition, and sends an unsolicited
|
||||||
|
interrupt to the CPU with "read buffer available" status. In response, RTE
|
||||||
|
sends an "acknowledge" command to the multiplexer, which returns the
|
||||||
|
"partial buffer" status and the buffer length. RTE then follows with a
|
||||||
|
"read buffer" command and prepares to receive the first buffer data.
|
||||||
|
Concurrently, the multiplexer begins filling the second 254-byte buffer.
|
||||||
|
|
||||||
|
RTE uses DCPC to transfer the data from the multiplexer, which is faster
|
||||||
|
than the incoming data rate. Therefore, the transfer completes while the
|
||||||
|
second buffer is still filling. On DCPC completion, the multiplexer checks
|
||||||
|
to see if the other receive buffer is complete, and if it is, issues
|
||||||
|
another unsolicited interrupt with "read buffer available" status.
|
||||||
|
|
||||||
|
The buffer completion check incorrectly returns TRUE if the buffer contains
|
||||||
|
received data. It should return TRUE only if the buffer contains data and
|
||||||
|
it is not currently being filled.
|
||||||
|
|
||||||
|
For short reads, such as EDIT screen reads, the second buffer fill
|
||||||
|
completes before the first buffer DCPC transfer, and the returned "read
|
||||||
|
buffer available" status is appropriate. For long reads, such as Kermit
|
||||||
|
transfers, the incorrect buffer check causes RTE to transfer data from the
|
||||||
|
second buffer while it is incomplete. This leads to corrupted data and
|
||||||
|
Kermit transmission errors.
|
||||||
|
|
||||||
|
RESOLUTION: Modify "mpx_cntl_svc" (hp2100_mpx.c) to indicate read buffer
|
||||||
|
availability only when filling is complete.
|
||||||
|
|
||||||
|
STATUS: Fixed in version 4.0-0.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
272. PROBLEM: An MPX binary read larger than 254 bytes never completes.
|
||||||
|
|
||||||
|
VERSION: 4.0-0.
|
||||||
|
|
||||||
|
OBSERVATION: The MPX device may be configured to terminate a read on
|
||||||
|
reception of either a special character (e.g., CR) or a certain character
|
||||||
|
count. Counts up to 64K bytes are permitted, and such reads will succeed
|
||||||
|
if the alternating buffers are concurrently transferred to the CPU before
|
||||||
|
filling completely. However, while reads that specify character counts of
|
||||||
|
254 or less complete as expected, reads of more than 254 bytes never
|
||||||
|
complete.
|
||||||
|
|
||||||
|
CAUSE: As each character is received, the current character count is
|
||||||
|
compared to the termination count, and the read completes if the counts are
|
||||||
|
equal. However, the current character count is determined by the number of
|
||||||
|
characters in the current buffer and not the accumulated count of all
|
||||||
|
characters received. As the read buffers are 254 bytes long, the current
|
||||||
|
count will never exceed 254. Therefore, any read terminating on a count
|
||||||
|
greater than that will never complete.
|
||||||
|
|
||||||
|
RESOLUTION: Define a new "mpx_termcnt" array to hold the termination
|
||||||
|
counts for the eight lines, and reassign the "mpx_charcnt" array to hold
|
||||||
|
the current character counts. Modify "exec_command" and "mpx_line_svc"
|
||||||
|
(hp2100_mpx.c) to set the termination count, compare it against the current
|
||||||
|
character count as characters are received, and terminate reception if
|
||||||
|
enabled and the values are equal.
|
||||||
|
|
||||||
|
STATUS: Fixed in version 4.0-0.
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
|
|
||||||
MPX 12792C 8-channel multiplexer card
|
MPX 12792C 8-channel multiplexer card
|
||||||
|
|
||||||
|
28-Jul-16 JDB Fixed buffer ready check at read completion
|
||||||
|
Fixed terminate on character counts > 254
|
||||||
13-May-16 JDB Modified for revised SCP API function parameter types
|
13-May-16 JDB Modified for revised SCP API function parameter types
|
||||||
24-Dec-14 JDB Added casts for explicit downward conversions
|
24-Dec-14 JDB Added casts for explicit downward conversions
|
||||||
10-Jan-13 MP Added DEV_MUX and additional DEVICE field values
|
10-Jan-13 MP Added DEV_MUX and additional DEVICE field values
|
||||||
|
@ -123,6 +125,7 @@
|
||||||
Because this is a functional simulation of the multiplexer and not a Z80
|
Because this is a functional simulation of the multiplexer and not a Z80
|
||||||
emulation, the diagnostic cannot be used to test the implementation.
|
emulation, the diagnostic cannot be used to test the implementation.
|
||||||
|
|
||||||
|
|
||||||
Implementation notes:
|
Implementation notes:
|
||||||
|
|
||||||
1. The 12792 had two baud-rate generators that were assigned to lines by the
|
1. The 12792 had two baud-rate generators that were assigned to lines by the
|
||||||
|
@ -508,7 +511,12 @@ BITFIELD (FL_DO_ENQACK, 0, 1) /* Port flags: do ENQ/AC
|
||||||
|
|
||||||
/* Multiplexer controller state variables */
|
/* Multiplexer controller state variables */
|
||||||
|
|
||||||
typedef enum { idle, cmd, param, exec } STATE;
|
typedef enum { /* execution state */
|
||||||
|
idle,
|
||||||
|
cmd,
|
||||||
|
param,
|
||||||
|
exec
|
||||||
|
} STATE;
|
||||||
|
|
||||||
STATE mpx_state = idle; /* controller state */
|
STATE mpx_state = idle; /* controller state */
|
||||||
|
|
||||||
|
@ -535,7 +543,8 @@ struct {
|
||||||
uint8 mpx_key [MPX_PORTS]; /* port keys */
|
uint8 mpx_key [MPX_PORTS]; /* port keys */
|
||||||
uint16 mpx_config [MPX_PORTS]; /* port configuration */
|
uint16 mpx_config [MPX_PORTS]; /* port configuration */
|
||||||
uint16 mpx_rcvtype [MPX_PORTS]; /* receive type */
|
uint16 mpx_rcvtype [MPX_PORTS]; /* receive type */
|
||||||
uint16 mpx_charcnt [MPX_PORTS]; /* character count */
|
uint16 mpx_charcnt [MPX_PORTS]; /* current character count */
|
||||||
|
uint16 mpx_termcnt [MPX_PORTS]; /* termination character count */
|
||||||
uint16 mpx_flowcntl [MPX_PORTS]; /* flow control */
|
uint16 mpx_flowcntl [MPX_PORTS]; /* flow control */
|
||||||
uint8 mpx_enq_cntr [MPX_PORTS]; /* ENQ character counter */
|
uint8 mpx_enq_cntr [MPX_PORTS]; /* ENQ character counter */
|
||||||
uint16 mpx_ack_wait [MPX_PORTS]; /* ACK wait timer */
|
uint16 mpx_ack_wait [MPX_PORTS]; /* ACK wait timer */
|
||||||
|
@ -675,6 +684,7 @@ REG mpx_reg [] = {
|
||||||
{ BRDATA (PCONFIG, mpx_config, 8, 16, MPX_PORTS) },
|
{ BRDATA (PCONFIG, mpx_config, 8, 16, MPX_PORTS) },
|
||||||
{ BRDATA (RCVTYPE, mpx_rcvtype, 8, 16, MPX_PORTS) },
|
{ BRDATA (RCVTYPE, mpx_rcvtype, 8, 16, MPX_PORTS) },
|
||||||
{ BRDATA (CHARCNT, mpx_charcnt, 8, 16, MPX_PORTS) },
|
{ BRDATA (CHARCNT, mpx_charcnt, 8, 16, MPX_PORTS) },
|
||||||
|
{ BRDATA (TERMCNT, mpx_termcnt, 8, 16, MPX_PORTS) },
|
||||||
{ BRDATA (FLOWCNTL, mpx_flowcntl, 8, 16, MPX_PORTS) },
|
{ BRDATA (FLOWCNTL, mpx_flowcntl, 8, 16, MPX_PORTS) },
|
||||||
|
|
||||||
{ BRDATA (ENQCNTR, mpx_enq_cntr, 10, 7, MPX_PORTS) },
|
{ BRDATA (ENQCNTR, mpx_enq_cntr, 10, 7, MPX_PORTS) },
|
||||||
|
@ -1128,17 +1138,21 @@ switch (mpx_cmd) {
|
||||||
if (port >= 0) /* port defined? */
|
if (port >= 0) /* port defined? */
|
||||||
buf_cancel (ioread, port, get); /* cancel get buffer */
|
buf_cancel (ioread, port, get); /* cancel get buffer */
|
||||||
|
|
||||||
if ((buf_avail (ioread, port) == 1) && /* one buffer remaining? */
|
if (buf_avail (ioread, port) == 2) /* if all buffers are now clear */
|
||||||
!(mpx_flags [port] & FL_RDFILL)) /* and not filling it? */
|
mpx_charcnt [port] = 0; /* then clear the current character count */
|
||||||
mpx_flags [port] |= FL_HAVEBUF; /* indicate buffer availability */
|
|
||||||
|
else if (!(mpx_flags [port] & FL_RDFILL)) /* otherwise if the other buffer is not filling */
|
||||||
|
mpx_flags [port] |= FL_HAVEBUF; /* then indicate buffer availability */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
case CMD_CANCEL_ALL: /* cancel all read buffers */
|
case CMD_CANCEL_ALL: /* cancel all read buffers */
|
||||||
port = key_to_port (mpx_portkey); /* get port */
|
port = key_to_port (mpx_portkey); /* get port */
|
||||||
|
|
||||||
if (port >= 0) /* port defined? */
|
if (port >= 0) { /* port defined? */
|
||||||
buf_init (ioread, port); /* reinitialize read buffers */
|
buf_init (ioread, port); /* reinitialize read buffers */
|
||||||
|
mpx_charcnt [port] = 0; /* and clear the current character count */
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
@ -1208,8 +1222,10 @@ switch (mpx_cmd) {
|
||||||
case CMD_SET_COUNT: /* set character count */
|
case CMD_SET_COUNT: /* set character count */
|
||||||
port = key_to_port (mpx_portkey); /* get port */
|
port = key_to_port (mpx_portkey); /* get port */
|
||||||
|
|
||||||
if (port >= 0) /* port defined? */
|
if (port >= 0) { /* port defined? */
|
||||||
mpx_charcnt [port] = (uint16) mpx_param; /* save port character count */
|
mpx_termcnt [port] = (uint16) mpx_param; /* save port termination character count */
|
||||||
|
mpx_charcnt [port] = 0; /* and clear the current character count */
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
@ -1262,13 +1278,14 @@ switch (mpx_cmd) {
|
||||||
if (port >= 0) /* port defined? */
|
if (port >= 0) /* port defined? */
|
||||||
if (buf_len (ioread, port, put) > 0) { /* any chars in buffer? */
|
if (buf_len (ioread, port, put) > 0) { /* any chars in buffer? */
|
||||||
buf_term (ioread, port, 0); /* terminate buffer and set header */
|
buf_term (ioread, port, 0); /* terminate buffer and set header */
|
||||||
|
mpx_charcnt [port] = 0; /* then clear the current character count */
|
||||||
|
|
||||||
if (buf_avail (ioread, port) == 1) /* first read buffer? */
|
if (buf_avail (ioread, port) == 1) /* first read buffer? */
|
||||||
mpx_flags [port] |= FL_HAVEBUF; /* indicate availability */
|
mpx_flags [port] |= FL_HAVEBUF; /* indicate availability */
|
||||||
}
|
}
|
||||||
|
|
||||||
else { /* buffer is empty */
|
else { /* buffer is empty */
|
||||||
mpx_charcnt [port] = 1; /* set to terminate on one char */
|
mpx_termcnt [port] = 1; /* set to terminate on one char */
|
||||||
mpx_flags [port] |= FL_ALERT; /* set alert flag */
|
mpx_flags [port] |= FL_ALERT; /* set alert flag */
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1520,8 +1537,9 @@ switch (mpx_state) { /* dispatch
|
||||||
if (buf_len (ioread, mpx_port, get) == 0) { /* buffer now empty? */
|
if (buf_len (ioread, mpx_port, get) == 0) { /* buffer now empty? */
|
||||||
buf_free (ioread, mpx_port); /* free buffer */
|
buf_free (ioread, mpx_port); /* free buffer */
|
||||||
|
|
||||||
if (buf_avail (ioread, mpx_port) == 1) /* another buffer available? */
|
if ((buf_avail (ioread, mpx_port) == 1) && /* one buffer remaining? */
|
||||||
mpx_flags [mpx_port] |= FL_HAVEBUF; /* indicate availability */
|
!(mpx_flags [mpx_port] & FL_RDFILL)) /* and not filling it? */
|
||||||
|
mpx_flags [mpx_port] |= FL_HAVEBUF; /* indicate buffer availability */
|
||||||
}
|
}
|
||||||
|
|
||||||
mpx_state = idle; /* idle controller */
|
mpx_state = idle; /* idle controller */
|
||||||
|
@ -1675,7 +1693,6 @@ const t_bool fast_binary_read = (mpx_cmd == CMD_BINARY_READ); /* fast binary r
|
||||||
|
|
||||||
uint8 ch;
|
uint8 ch;
|
||||||
int32 chx;
|
int32 chx;
|
||||||
uint16 read_length;
|
|
||||||
t_stat status = SCPE_OK;
|
t_stat status = SCPE_OK;
|
||||||
t_bool recv_loop = !fast_binary_read; /* bypass if fast binary read */
|
t_bool recv_loop = !fast_binary_read; /* bypass if fast binary read */
|
||||||
t_bool xmit_loop = !(fast_binary_read || /* bypass if fast read or output suspended */
|
t_bool xmit_loop = !(fast_binary_read || /* bypass if fast read or output suspended */
|
||||||
|
@ -1854,25 +1871,26 @@ while (recv_loop) { /* OK to process? */
|
||||||
recv_loop = TRUE; /* no termination */
|
recv_loop = TRUE; /* no termination */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recv_loop) /* no termination condition? */
|
if (recv_loop) { /* no termination condition? */
|
||||||
buf_put (ioread, port, ch); /* put character in buffer */
|
buf_put (ioread, port, ch); /* put character in buffer */
|
||||||
|
mpx_charcnt [port]++; /* and count it */
|
||||||
read_length = buf_len (ioread, port, put); /* get current buffer length */
|
}
|
||||||
|
|
||||||
if ((rt & RT_END_ON_CNT) && /* end on count */
|
if ((rt & RT_END_ON_CNT) && /* end on count */
|
||||||
(read_length == mpx_charcnt [port])) { /* and count reached? */
|
(mpx_charcnt [port] == mpx_termcnt [port])) { /* and termination count reached? */
|
||||||
recv_loop = FALSE; /* set termination */
|
recv_loop = FALSE; /* set termination */
|
||||||
mpx_param = 0; /* no extra termination info */
|
mpx_param = 0; /* no extra termination info */
|
||||||
|
mpx_charcnt [port] = 0; /* clear the current character count */
|
||||||
|
|
||||||
if (mpx_flags [port] & FL_ALERT) { /* was this alert for term rcv buffer? */
|
if (mpx_flags [port] & FL_ALERT) { /* was this alert for term rcv buffer? */
|
||||||
mpx_flags [port] &= ~FL_ALERT; /* clear alert flag */
|
mpx_flags [port] &= ~FL_ALERT; /* clear alert flag */
|
||||||
mpx_charcnt [port] = RD_BUF_LIMIT; /* reset character count */
|
mpx_termcnt [port] = RD_BUF_LIMIT; /* reset termination character count */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (read_length == RD_BUF_LIMIT) { /* buffer now full? */
|
else if (buf_len (ioread, port, put) == RD_BUF_LIMIT) { /* buffer now full? */
|
||||||
recv_loop = FALSE; /* set termination */
|
recv_loop = FALSE; /* set termination */
|
||||||
mpx_param = mpx_param | RS_PARTIAL; /* and partial buffer flag */
|
mpx_param = mpx_param | RS_PARTIAL; /* and partial buffer flag */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recv_loop) /* no termination condition? */
|
if (recv_loop) /* no termination condition? */
|
||||||
|
@ -1887,7 +1905,7 @@ while (recv_loop) { /* OK to process? */
|
||||||
else if (rt & RT_END_ON_CHAR)
|
else if (rt & RT_END_ON_CHAR)
|
||||||
fprintf (sim_deb, "character %s\n", fmt_char (ch));
|
fprintf (sim_deb, "character %s\n", fmt_char (ch));
|
||||||
else
|
else
|
||||||
fprintf (sim_deb, "count = %d\n", mpx_charcnt [port]);
|
fprintf (sim_deb, "count = %d\n", mpx_termcnt [port]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buf_len (ioread, port, put) == 0) { /* zero-length read? */
|
if (buf_len (ioread, port, put) == 0) { /* zero-length read? */
|
||||||
|
@ -2070,7 +2088,7 @@ return SCPE_OK;
|
||||||
|
|
||||||
A direct attach to the poll unit is only allowed when restoring a previously
|
A direct attach to the poll unit is only allowed when restoring a previously
|
||||||
saved session.
|
saved session.
|
||||||
|
|
||||||
The Telnet poll service routine is synchronized with the other input polling
|
The Telnet poll service routine is synchronized with the other input polling
|
||||||
devices in the simulator to facilitate idling.
|
devices in the simulator to facilitate idling.
|
||||||
*/
|
*/
|
||||||
|
@ -2227,7 +2245,8 @@ for (i = 0; i < MPX_PORTS; i++) { /* clear per-line variab
|
||||||
mpx_config [i] = (uint16) (SK_PWRUP_1 | i);
|
mpx_config [i] = (uint16) (SK_PWRUP_1 | i);
|
||||||
|
|
||||||
mpx_rcvtype [i] = RT_PWRUP; /* power on config for echoplex */
|
mpx_rcvtype [i] = RT_PWRUP; /* power on config for echoplex */
|
||||||
mpx_charcnt [i] = 0; /* default character count */
|
mpx_charcnt [i] = 0; /* clear character count */
|
||||||
|
mpx_termcnt [i] = 0; /* default termination character count */
|
||||||
mpx_flowcntl [i] = 0; /* default flow control */
|
mpx_flowcntl [i] = 0; /* default flow control */
|
||||||
mpx_flags [i] = 0; /* clear state flags */
|
mpx_flags [i] = 0; /* clear state flags */
|
||||||
mpx_enq_cntr [i] = 0; /* clear ENQ counter */
|
mpx_enq_cntr [i] = 0; /* clear ENQ counter */
|
||||||
|
|
Binary file not shown.
Loading…
Add table
Reference in a new issue