HP2100: Fix MPX behaviors from Dave Bryan

This commit is contained in:
Mark Pizzolato 2016-07-31 10:07:52 -07:00
parent a59cd3bd3b
commit 592deb8f03
3 changed files with 123 additions and 24 deletions

View file

@ -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.

View file

@ -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,23 +1871,24 @@ 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 */
} }
@ -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? */
@ -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.