TMXR: Update tmxr_set_lnorder and add test of same

This commit is contained in:
Mark Pizzolato 2021-06-08 13:07:03 -07:00
parent 7dda50344c
commit 287ebb3ea8

View file

@ -5146,117 +5146,157 @@ return SCPE_OK;
/* Set the line connection order. /* Set the line connection order.
Example command for eight-line multiplexer: This validation routine is called to set the connection order for the
multiplexer whose TMXR pointer is passed in the "desc" parameter. It parses
the line order list, specified by the "cptr" parameter, of commands such as:
SET <dev> LINEORDER=1;5;2-4;7 SET <dev> LINEORDER=4-7
SET <dev> LINEORDER=1;5;2-4;7;ALL
SET <dev> LINEORDER=ALL
Resulting connection order: 1,5,2,3,4,7,0,6. Assuming an 8-channel multiplexer, the first form sets the connection order
to line numbers 4, 5, 6, and 7. The remaining lines will not be connected; a
connection attempt will be refused with "All connections busy." The second
form sets the connection order to line 1, 5, 2, 3, 4, 7, 0, and 6. The
trailing "ALL" parameter causes any unspecified lines to be added to the
connection order in ascending value. The third form sets the order to lines
0-7, which is the default order in the absence of a line connection order
array.
Parameters: The range of accepted line numbers, including those implied by "ALL", can be
- uptr = (not used) restricted by specifying a non-zero "val" parameter, with the upper 16 bits
- val = (not used) specifying the maximum line number, and the lower 16 bits specifying the
- cptr = pointer to first character of range specification minimum line number. If a minimum is specified but a maximum is not (i.e.,
- desc = pointer to multiplexer's TMXR structure is zero), the maximum is the last line number defined by the multiplexer
descriptor.
On entry, cptr points to the value portion of the command string, which may The "uptr" parameter is not used.
be either a semicolon-separated list of line ranges or the keyword ALL.
On entry, "cptr" points to the value portion of the command string, which may
be either a semicolon-separated list of line ranges or the keyword "ALL". If
"ALL" is specified, it must be the last (or only) item in the list.
If a line connection order array is not defined in the multiplexer If a line connection order array is not defined in the multiplexer
descriptor, the command is rejected. If the specified range encompasses all descriptor, or a line range string is not present, or the optional minimum
of the lines, the first value of the connection order array is set to -1 to and maximum restrictions in the "val" parameter are not valid, the command is
rejected. If the specified range encompasses all of the lines defined by the
multiplexer, the first value of the connection order array is set to -1 to
indicate sequential connection order. Otherwise, the line values in the indicate sequential connection order. Otherwise, the line values in the
array are set to the order specified by the command string. All values are array are set to the order specified by the command string. If fewer values
populated, first with those explicitly specified in the command string, and are supplied than there are lines supported by the device, and the final
then in ascending sequence with those not specified. parameter is not ALL, the remaining lines will be inaccessible and are
indicated by a -1 value after the last specified value.
If an error occurs, the original line order is not disturbed. If an error occurs, the original line order is not disturbed.
*/ */
t_stat tmxr_set_lnorder (UNIT *uptr, int32 val, CONST char *carg, void *desc) t_stat tmxr_set_lnorder (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{ {
TMXR *mp = (TMXR *) desc; TMXR *mp = (TMXR *) desc;
char *tbuf; char *tbuf = NULL;
char *tptr; char *tptr;
CONST char *cptr; t_addr low, high, min, max;
t_addr low, high, max = (t_addr) mp->lines - 1; int32 *list;
int32 *list;
t_bool *set; t_bool *set;
uint32 line, idx = 0; uint32 line, idx;
t_addr lncount = (t_addr) (mp->lines - 1);
t_stat result = SCPE_OK; t_stat result = SCPE_OK;
if (mp->lnorder == NULL) /* line connection order undefined? */ if (mp->lnorder == NULL) /* if the connection order array is not defined */
return SCPE_NXPAR; /* "Non-existent parameter" error */ return SCPE_NXPAR; /* then report a "Non-existent parameter" error */
else if ((carg == NULL) || (*carg == '\0')) /* line range not supplied? */ else if ((cptr == NULL) || (*cptr == '\0')) /* otherwise if a line range was not supplied */
return SCPE_MISVAL; /* "Missing value" error */ return SCPE_MISVAL; /* then report a "Missing value" error */
list = (int32 *) calloc (mp->lines, sizeof (int32)); /* allocate new line order array */ else { /* otherwise */
min = (t_addr) (val & 0xFFFF); /* split the restriction into */
max = (t_addr) ((val >> 16) & 0xFFFF); /* minimum and maximum line numbers */
if (list == NULL) /* allocation failed? */ if (max == 0) /* if the maximum line number isn't specified */
return SCPE_MEM; /* report it */ max = lncount; /* then use the defined maximum */
set = (t_bool *) calloc (mp->lines, sizeof (t_bool)); /* allocate line set tracking array */ if (min > lncount || max > lncount || min > max) /* if the restriction isn't valid */
return SCPE_IERR; /* then report an "Internal error" */
if (set == NULL) { /* allocation failed? */
free (list); /* free successful list allocation */
return SCPE_MEM; /* report it */
} }
tbuf = (char *) calloc (strlen(carg)+2, sizeof(*carg)); list = (int32 *) calloc (mp->lines, sizeof (int32)); /* allocate the new line order array */
strcpy (tbuf, carg);
if (list == NULL) /* if the allocation failed */
return SCPE_MEM; /* then report a "Memory exhausted" error */
set = (t_bool *) calloc (mp->lines, sizeof (t_bool)); /* allocate the line set tracking array */
if (set == NULL) { /* if the allocation failed */
free (list); /* then free the successful list allocation */
return SCPE_MEM; /* and report a "Memory exhausted" error */
}
tbuf = (char *) calloc (strlen(cptr)+2, sizeof(*cptr));
if (tbuf == NULL) { /* if the allocation failed */
free (tbuf); /* then free the successful list allocation */
return SCPE_MEM; /* and report a "Memory exhausted" error */
}
strcpy (tbuf, cptr);
tptr = tbuf + strlen (tbuf); /* append a semicolon */ tptr = tbuf + strlen (tbuf); /* append a semicolon */
*tptr++ = ';'; /* to the command string */ *tptr++ = ';'; /* to the command string */
*tptr = '\0'; /* to make parsing easier for get_range */ *tptr = '\0'; /* to make parsing easier for get_range */
cptr = tbuf; cptr = tbuf;
while (*cptr) { /* parse command string */ idx = 0; /* initialize the index of ordered values */
cptr = get_range (NULL, cptr, &low, &high, 10, max, ';');/* get a line range */
if (cptr == NULL) { /* parsing error? */ while (*cptr != '\0') { /* while characters remain in the command string */
result = SCPE_ARG; /* "Invalid argument" error */ if (strncasecmp (cptr, "ALL;", 4) == 0) { /* if the parameter is "ALL" */
break; if (val != 0 || idx > 0 && idx <= max) /* then if some lines are restrictied or unspecified */
for (line = (uint32) min; line <= max; line++) /* then fill them in sequentially */
if (set [line] == FALSE) /* setting each unspecified line */
list [idx++] = line; /* into the line order */
cptr = cptr + 4; /* advance past "ALL" and the trailing semicolon */
if (*cptr != '\0') /* if "ALL" is not the last parameter */
result = sim_messagef (SCPE_2MARG, /* then report extraneous items */
"Too many args: %s\n", cptr);
break; /* "ALL" terminates the order list */
} }
else if ((low > max) || (high > max)) { /* line out of range? */ cptr = get_range (NULL, cptr, &low, &high, 10, max, ';'); /* get a line range */
result = SCPE_SUB; /* "Subscript out of range" error */
break; if (cptr == NULL) { /* if a parsing error occurred */
result = SCPE_ARG; /* then report an invalid argument */
break; /* and terminate the parse */
} }
else if ((low == 0) && (high == max)) { /* entire line range specified? */ else if (low < min || low > max || high > max) { /* otherwise if the line number is invalid */
list [0] = -1; /* set sequential order flag */ result = SCPE_SUB; /* then report the subscript is out of range */
idx = (uint32) max + 1; /* indicate no fill-in needed */ break; /* and terminate the parse */
break;
} }
else else /* otherwise it's a valid range */
for (line = (uint32) low; line <= (uint32) high; line++) /* see if previously specified */ for (line = (uint32) low; line <= high; line++) /* so add the line(s) to the order */
if (set [line] == FALSE) { /* not already specified? */ if (set [line] == FALSE) { /* if the line number has not been specified */
set [line] = TRUE; /* now it is */ set [line] = TRUE; /* then now it is */
list [idx] = line; /* add line to connection order */ list [idx++] = line; /* and add it to the connection order */
idx = idx + 1; /* bump "specified" count */
} }
} }
if (result == SCPE_OK) { /* assignment successful? */ if (result == SCPE_OK) { /* if the assignment succeeded */
if (idx <= max) /* any lines not specified? */ if (idx <= max) /* then if any lines were not specified */
for (line = 0; line <= max; line++) /* fill them in sequentially */ list [idx] = -1; /* then terminate the order list after the last one */
if (set [line] == FALSE) { /* specified? */
list [idx] = line; /* no, so add it */
idx = idx + 1;
}
memcpy (mp->lnorder, list, mp->lines * sizeof (int32)); /* copy working array to connection array */ memcpy (mp->lnorder, list, /* copy the working array to the connection array */
mp->lines * sizeof (int32));
} }
free (list); /* free list allocation */ free (list); /* free the list allocation */
free (set); /* free set allocation */ free (set); /* and the set allocation */
free (tbuf); /* free arg copy with ; */ free (tbuf); /* and the arg copy with ; */
return result; return result; /* return the status */
} }
/* Show the line connection order.
/* Show line connection order.
Parameters: Parameters:
- st = stream on which output is to be written - st = stream on which output is to be written
@ -5268,7 +5308,8 @@ return result;
command is rejected. If the first value of the connection order array is set command is rejected. If the first value of the connection order array is set
to -1, then the connection order is sequential. Otherwise, the line values to -1, then the connection order is sequential. Otherwise, the line values
in the array are printed as a semicolon-separated list. Ranges are printed in the array are printed as a semicolon-separated list. Ranges are printed
where possible to shorten the output. where possible to shorten the output. A -1 value within the array indicates
the end of the order list.
*/ */
t_stat tmxr_show_lnorder (FILE *st, UNIT *uptr, int32 val, CONST void *desc) t_stat tmxr_show_lnorder (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
@ -5287,7 +5328,7 @@ if (*iptr < 0) /* sequential order indi
else { else {
low = last = *iptr++; /* set first line value */ low = last = *iptr++; /* set first line value */
for (j = 1; j <= mp->lines; j++) { /* print remaining lines in order list */ for (j = 1; last != -1; j++) { /* print the remaining lines in the order list */
if (j < mp->lines) /* more lines to process? */ if (j < mp->lines) /* more lines to process? */
i = *iptr++; /* get next line in list */ i = *iptr++; /* get next line in list */
else /* final iteration */ else /* final iteration */
@ -5622,6 +5663,64 @@ if ((dptr) && (dbits & dptr->dctrl)) {
/* Testing of sim_sock and tmxr */ /* Testing of sim_sock and tmxr */
static struct lnorder_test {
const char *orderspec;
int32 valspec;
t_stat expected_stat;
int32 expected_orderlist[8];
} lnorders[] = {
{NULL, 0, SCPE_MISVAL},
{"", 0, SCPE_MISVAL},
{"4-7", 0x3FFF3FFF, SCPE_IERR},
{"6-8", 0, SCPE_SUB},
{"9-11", 0, SCPE_SUB},
{"4-7", 0, SCPE_OK,
{ 4, 5, 6, 7, -1}},
{"1;5;2-4;7;ALL", 0, SCPE_OK,
{ 1, 5, 2, 3, 4, 7, 0, 6}},
{"ALL", 0, SCPE_OK,
{ -1}},
};
static t_stat _lnorder_test (TMXR *tmxr,
struct lnorder_test *t)
{
t_stat r;
char msg[80] = "";
int i;
r = tmxr_set_lnorder (NULL, t->valspec, t->orderspec, tmxr);
if (r != t->expected_stat) {
snprintf (msg, sizeof (msg), "Unexpected lnorder result status for \"%s\" Expected: %s", t->orderspec, sim_error_text (t->expected_stat));
return sim_messagef (SCPE_ARG, "%s, Got: %s\n", msg, sim_error_text (r));
}
if (r == SCPE_OK) {
for (i = 0; i<8; i++)
if (t->expected_orderlist[i] != tmxr->lnorder[i])
return sim_messagef (SCPE_ARG, "Unexpected order entry for line %d: %d vs %d\n", i, tmxr->lnorder[i], t->expected_orderlist[i]);
}
return SCPE_OK;
}
static t_stat sim_tmxr_test_lnorder (TMXR *tmxr)
{
int i;
int32 *saved_lnorder = tmxr->lnorder;
int32 saved_lines = tmxr->lines;
tmxr->lnorder = calloc (tmxr->lines, sizeof (*tmxr->lnorder));
if (tmxr->lines >= 8) {
tmxr->lines = 8;
for (i = 0; i < (sizeof (lnorders)/sizeof (lnorders[0])); ++i)
_lnorder_test (tmxr, &lnorders[i]);
}
free (tmxr->lnorder);
tmxr->lnorder = saved_lnorder;
tmxr->lines = saved_lines;
return SCPE_OK;
}
#include <setjmp.h> #include <setjmp.h>
t_stat tmxr_sock_test (DEVICE *dptr) t_stat tmxr_sock_test (DEVICE *dptr)
@ -5676,6 +5775,7 @@ if (tmxr->lines > 1) {
sim_close_sock (sock_line); sim_close_sock (sock_line);
sock_line = INVALID_SOCKET; sock_line = INVALID_SOCKET;
SIM_TEST(detach_cmd (0, dptr->name)); SIM_TEST(detach_cmd (0, dptr->name));
SIM_TEST(sim_tmxr_test_lnorder (tmxr));
} }
return stat; return stat;
} }