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
==================================
Last update: 2016-03-04
Last update: 2016-07-28
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.
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
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
24-Dec-14 JDB Added casts for explicit downward conversions
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
emulation, the diagnostic cannot be used to test the implementation.
Implementation notes:
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 */
typedef enum { idle, cmd, param, exec } STATE;
typedef enum { /* execution state */
idle,
cmd,
param,
exec
} STATE;
STATE mpx_state = idle; /* controller state */
@ -535,7 +543,8 @@ struct {
uint8 mpx_key [MPX_PORTS]; /* port keys */
uint16 mpx_config [MPX_PORTS]; /* port configuration */
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 */
uint8 mpx_enq_cntr [MPX_PORTS]; /* ENQ character counter */
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 (RCVTYPE, mpx_rcvtype, 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 (ENQCNTR, mpx_enq_cntr, 10, 7, MPX_PORTS) },
@ -1128,17 +1138,21 @@ switch (mpx_cmd) {
if (port >= 0) /* port defined? */
buf_cancel (ioread, port, get); /* cancel get buffer */
if ((buf_avail (ioread, port) == 1) && /* one buffer remaining? */
!(mpx_flags [port] & FL_RDFILL)) /* and not filling it? */
mpx_flags [port] |= FL_HAVEBUF; /* indicate buffer availability */
if (buf_avail (ioread, port) == 2) /* if all buffers are now clear */
mpx_charcnt [port] = 0; /* then clear the current character count */
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;
case CMD_CANCEL_ALL: /* cancel all read buffers */
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 */
mpx_charcnt [port] = 0; /* and clear the current character count */
}
break;
@ -1208,8 +1222,10 @@ switch (mpx_cmd) {
case CMD_SET_COUNT: /* set character count */
port = key_to_port (mpx_portkey); /* get port */
if (port >= 0) /* port defined? */
mpx_charcnt [port] = (uint16) mpx_param; /* save port character count */
if (port >= 0) { /* port defined? */
mpx_termcnt [port] = (uint16) mpx_param; /* save port termination character count */
mpx_charcnt [port] = 0; /* and clear the current character count */
}
break;
@ -1262,13 +1278,14 @@ switch (mpx_cmd) {
if (port >= 0) /* port defined? */
if (buf_len (ioread, port, put) > 0) { /* any chars in buffer? */
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? */
mpx_flags [port] |= FL_HAVEBUF; /* indicate availability */
}
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 */
}
break;
@ -1520,8 +1537,9 @@ switch (mpx_state) { /* dispatch
if (buf_len (ioread, mpx_port, get) == 0) { /* buffer now empty? */
buf_free (ioread, mpx_port); /* free buffer */
if (buf_avail (ioread, mpx_port) == 1) /* another buffer available? */
mpx_flags [mpx_port] |= FL_HAVEBUF; /* indicate availability */
if ((buf_avail (ioread, mpx_port) == 1) && /* one buffer remaining? */
!(mpx_flags [mpx_port] & FL_RDFILL)) /* and not filling it? */
mpx_flags [mpx_port] |= FL_HAVEBUF; /* indicate buffer availability */
}
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;
int32 chx;
uint16 read_length;
t_stat status = SCPE_OK;
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 */
@ -1854,25 +1871,26 @@ while (recv_loop) { /* OK to process? */
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 */
read_length = buf_len (ioread, port, put); /* get current buffer length */
mpx_charcnt [port]++; /* and count it */
}
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 */
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? */
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? */
recv_loop = FALSE; /* set termination */
mpx_param = mpx_param | RS_PARTIAL; /* and partial buffer flag */
else if (buf_len (ioread, port, put) == RD_BUF_LIMIT) { /* buffer now full? */
recv_loop = FALSE; /* set termination */
mpx_param = mpx_param | RS_PARTIAL; /* and partial buffer flag */
}
if (recv_loop) /* no termination condition? */
@ -1887,7 +1905,7 @@ while (recv_loop) { /* OK to process? */
else if (rt & RT_END_ON_CHAR)
fprintf (sim_deb, "character %s\n", fmt_char (ch));
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? */
@ -2227,7 +2245,8 @@ for (i = 0; i < MPX_PORTS; i++) { /* clear per-line variab
mpx_config [i] = (uint16) (SK_PWRUP_1 | i);
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_flags [i] = 0; /* clear state flags */
mpx_enq_cntr [i] = 0; /* clear ENQ counter */

Binary file not shown.