1
0
mirror of https://github.com/rcornwell/sims.git synced 2026-01-31 05:42:51 +00:00
Files
rcornwell.sims/IBM360/ibm360_scom.c
2024-02-15 14:13:09 -05:00

670 lines
23 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ibm360_scom.c: IBM 360 3271 scommunications controller
Copyright (c) 2017-2020, Richard Cornwell
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
RICHARD CORNWELL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "ibm360_defs.h"
#include "sim_sock.h"
#include "sim_tmxr.h"
#ifdef NUM_DEVS_COM
#define UNIT_COM UNIT_DISABLE
/* u3 */
#define CMD_WR 0x01 /* Write data to com line */
#define CMD_RD 0x02 /* Read buffer */
#define CMD_NOP 0x03 /* Nop scommand */
#define CMD_WRER 0x05 /* Erase and write data */
#define CMD_RDMD 0x06 /* Read modified */
#define CMD_SEL 0x0B /* Select */
#define CMD_WRERALT 0x0D /* Write erase alternative */
#define CMD_EAU 0x0F /* Erase all un protected */
#define CMD_WSF 0x11 /* Writye structured field */
/* u3 second byte */
#define RECV 0x00100 /* Recieving data */
#define SEND 0x00200 /* Sending data */
#define ENAB 0x00400 /* Line enabled */
#define DATA 0x00800 /* Data available */
#define INIT1 0x01000 /* Send DO EOR, waiting WILL EOR */
#define INPUT 0x02000 /* Input ready */
#define ATTN 0x04000 /* Send attention signal */
#define HALT 0x08000 /* Halt operation */
/* Upper 11 bits of u3 hold the device address */
/* u4 */
/* Where we are reading from */
/* u5 */
/* Sense byte 0 */
#define SNS_CMDREJ 0x80 /* Command reject */
#define SNS_INTVENT 0x40 /* Unit intervention required */
#define SNS_BUSCHK 0x20 /* Parity error on bus */
#define SNS_EQUCHK 0x10 /* Equipment check */
#define SNS_DATCHK 0x08 /* Data Check */
#define SNS_UNITSPC 0x04 /* Specific to unit */
#define SNS_CTLCHK 0x02 /* Timeout on device */
#define SNS_OPRCHK 0x01 /* Invalid operation to device */
/* u6 */
/* Pointer into buffer */
#define CMD u3
#define IPTR u4
#define SNS u5
#define BPTR u6
#define TC_WILL 0x1 /* Option in will state */
#define TC_WONT 0x2 /* Wont do option */
#define TC_DO 0x4 /* Will do option. */
#define TC_DONT 0x8 /* Dont do option */
#define IAC 255 /* Interpret as command */
#define DONT 254 /* Dont use option */
#define DO 253 /* Use this option */
#define WONT 252 /* I wont use this option */
#define WILL 251 /* I will use this option */
#define IP 244 /* Interrupt pending */
#define BREAK 243 /* Break */
#define EOR 239 /* End of record */
/* Telnet options we care about */
#define OPTION_BINARY 0 /* Send 8 bit data */
#define OPTION_ECHO 1 /* Echo */
#define OPTION_SGA 3 /* Set go ahead */
#define OPTION_TERMINAL 24 /* Request terminal type */
#define OPTION_EOR 25 /* Handle end of record */
#define TS_DATA 0 /* Regular state */
#define TS_IAC 1 /* Have seen IAC */
#define TS_WILL 2 /* Have seen IAC WILL */
#define TS_WONT 3 /* Have seen IAC WONT */
#define TS_DO 4 /* Have seen IAC DO */
#define TS_DONT 5 /* Have seen IAC DONT */
/* Remote orders */
#define REMOTE_EAU 0x6F /* Erase all unprotected */
#define REMOTE_EW 0xF5 /* Erase/Write */
#define REMOTE_RB 0xF2 /* Read Buffer */
#define REMOTE_RM 0x6e /* Read Modified */
#define REMOTE_WRERALT 0x7e /* Write erase alternative */
#define REMOTE_WRT 0xF1 /* Write */
#define REMOTE_WSF 0xF3 /* Write structured field */
struct _line {
uint16 option_state[256]; /* Current telnet state */
uint8 state; /* Current line status */
} line_data[NUM_UNITS_SCOM];
extern int32 tmxr_poll;
uint8 scoml_startcmd(UNIT *uptr, uint8 cmd) ;
uint8 scoml_haltio(UNIT *uptr);
t_stat scoml_srv(UNIT *uptr);
t_stat scom_reset(DEVICE *dptr);
t_stat scom_scan(UNIT *uptr);
t_stat scom_readinput(UNIT *uptr);
void scom_sendoption(UNIT *uptr, int unit, uint8 state, uint8 opt);
t_stat scom_attach(UNIT *uptr, CONST char *);
t_stat scom_detach(UNIT *uptr);
t_stat scom_help (FILE *, DEVICE *, UNIT *, int32, const char *);
const char *scom_description (DEVICE *);
uint8 scom_buf[NUM_UNITS_SCOM][256];
TMLN scom_ldsc[NUM_UNITS_SCOM];
TMXR scom_desc = { NUM_UNITS_SCOM, 0, 0, scom_ldsc};
MTAB scom_mod[] = {
{0}
};
MTAB scoml_mod[] = {
{MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "DEV", "DEV", &set_dev_addr,
&show_dev_addr, NULL},
{0}
};
UNIT scom_unit[] = {
{UDATA(&scom_scan, UNIT_ATTABLE | UNIT_DISABLE | UNIT_IDLE, 0)}, /* Line scanner */
};
UNIT scoml_unit[] = {
{UDATA(&scoml_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x050)}, /* 0 */
{UDATA(&scoml_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x051)}, /* 1 */
{UDATA(&scoml_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x052)}, /* 2 */
{UDATA(&scoml_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x053)}, /* 3 */
{UDATA(&scoml_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x054)}, /* 4 */
{UDATA(&scoml_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x055)}, /* 5 */
{UDATA(&scoml_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x056)}, /* 6 */
{UDATA(&scoml_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x057)}, /* 7 */
{UDATA(&scoml_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x058)}, /* 8 */
{UDATA(&scoml_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x059)}, /* 9 */
{UDATA(&scoml_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x05A)}, /* A */
{UDATA(&scoml_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x05B)}, /* B */
{UDATA(&scoml_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x05C)}, /* C */
{UDATA(&scoml_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x05D)}, /* D */
{UDATA(&scoml_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x05E)}, /* E */
{UDATA(&scoml_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x05F)}, /* F */
};
struct dib scom_dib = { 0xF0, NUM_UNITS_SCOM, NULL, scoml_startcmd,
scoml_haltio, scoml_unit, NULL};
DEVICE scom_dev = {
"SCOM", scom_unit, NULL, scom_mod,
NUM_DEVS_SCOM, 8, 15, 1, 8, 8,
NULL, NULL, scom_reset, NULL, &scom_attach, &scom_detach,
NULL, DEV_MUX | DEV_DISABLE | DEV_DEBUG, 0, dev_debug,
NULL, NULL, &scom_help, NULL, NULL, &scom_description
};
DEVICE scoml_dev = {
"SCOML", scoml_unit, NULL, scoml_mod,
NUM_UNITS_SCOM, 8, 15, 1, 8, 8,
NULL, NULL, NULL, NULL, NULL, NULL,
&scom_dib, DEV_DISABLE | DEV_DEBUG, 0, dev_debug
};
/*
* Issue a scommand to the 3271 controller.
*/
uint8 scoml_startcmd(UNIT *uptr, uint8 cmd) {
DEVICE *dptr = find_dev_from_unit(uptr);
int unit = (uptr - dptr->units);
sim_debug(DEBUG_CMD, dptr, "CMD unit=%d %x\n", unit, cmd);
if ((uptr->CMD & 0xff) != 0) {
return SNS_BSY;
}
switch (cmd & 0x3) {
case 0x2: /* Read scommand */
case 0x1: /* Write scommand */
case 0x3: /* Control */
if (cmd != CMD_NOP)
uptr->SNS = 0;
uptr->CMD |= cmd;
sim_activate(uptr, 200);
return 0;
case 0x0: /* Status */
if (cmd == 0x4) { /* Sense */
uptr->CMD |= cmd;
sim_activate(uptr, 200);
return 0;
}
break;
}
if (uptr->SNS & 0xff)
return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK;
return SNS_CHNEND|SNS_DEVEND;
}
/*
* Handle halt I/O instruction by stoping running scommand.
*/
uint8 scoml_haltio(UNIT *uptr) {
uint16 addr = GET_UADDR(uptr->CMD);
DEVICE *dptr = find_dev_from_unit(uptr);
int unit = (uptr - dptr->units);
int cmd = uptr->CMD & 0xff;
sim_debug(DEBUG_CMD, dptr, "HLTIO unit=%d %x\n", unit, cmd);
if ((scom_unit[0].flags & UNIT_ATT) == 0) /* attached? */
return 3;
switch (cmd) {
case 0:
case 0x4:
case CMD_SEL: /* Select */
case CMD_NOP: /* Nop scommand */
/* Short scommands nothing to do */
break;
case CMD_WR: /* Write data to com line */
case CMD_RD: /* Read buffer */
case CMD_WRER: /* Erase and write data */
case CMD_RDMD: /* Read modified */
case CMD_EAU: /* Erase all un protected */
case CMD_WSF: /* Write Structured field */
case CMD_WRERALT: /* Write erase alternative */
uptr->CMD |= HALT;
chan_end(addr, SNS_CHNEND|SNS_DEVEND);
sim_activate(uptr, 20);
break;
}
return 1;
}
/* Handle per unit scommands */
t_stat scoml_srv(UNIT * uptr)
{
uint16 addr = GET_UADDR(uptr->CMD);
DEVICE *dptr = find_dev_from_unit(uptr);
int unit = (uptr - dptr->units);
int cmd = uptr->CMD & 0xff;
uint8 ch;
if (scom_ldsc[unit].conn == 0 && cmd != 0x4) {
/* If no connection yet, pretend unit is powered off.
ATTN & DE at connection will revive activity. */
uptr->SNS |= SNS_INTVENT;
uptr->CMD &= ~0xff;
chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
return SCPE_OK;
}
if ((uptr->CMD & (RECV|DATA)) != 0) {
sim_activate(uptr, 200);
return scom_readinput(uptr);
}
switch (cmd) {
case 0:
break;
case 0x4:
ch = uptr->SNS & 0xff;
sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 1 %x\n", unit, ch);
chan_write_byte(addr, &ch) ;
uptr->CMD &= ~0xff;
chan_end(addr, SNS_CHNEND|SNS_DEVEND);
break;
case CMD_RDMD: /* Read modified */
case CMD_RD: /* Read in data from scom line */
uptr->SNS = 0;
if (uptr->CMD & HALT) {
uptr->CMD &= ~(0xFF|RECV);
return SCPE_OK;
}
if (uptr->CMD & ENAB) {
if (scom_ldsc[unit].conn == 0) {
sim_debug(DEBUG_DETAIL, dptr, "unit=%d disco\n", unit);
uptr->CMD &= ~(0xff|INPUT|ENAB|RECV|INIT1|SEND|DATA);
uptr->SNS = SNS_CTLCHK;
uptr->BPTR = 0;
uptr->IPTR = 0;
chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP);
return SCPE_OK;
}
if ((uptr->CMD & RECV) == 0) {
/* Send cmd IAC EOR */
if (tmxr_rqln(&scom_ldsc[unit]) == 0) {
sim_debug(DEBUG_DETAIL, dptr, "unit=%d Send read cmd %x\n", unit, cmd);
if (cmd == CMD_RD)
tmxr_putc_ln( &scom_ldsc[unit], REMOTE_RB);
else
tmxr_putc_ln( &scom_ldsc[unit], REMOTE_RM);
tmxr_putc_ln( &scom_ldsc[unit], IAC);
tmxr_putc_ln( &scom_ldsc[unit], EOR);
}
uptr->CMD |= RECV;
}
sim_activate(uptr, 200);
}
break;
case CMD_WRERALT: /* Write erase alternative */
ch = REMOTE_WRERALT;
goto write;
case CMD_WSF: /* Write structured field */
ch = REMOTE_WSF;
goto write;
case CMD_WRER: /* Erase and write data */
ch = REMOTE_EW;
goto write;
case CMD_EAU: /* Erase all un protected */
ch = REMOTE_EAU;
goto write;
case CMD_WR: /* Write data to com line */
ch = REMOTE_WRT;
write:
uptr->SNS = 0;
if (uptr->CMD & HALT) {
uptr->CMD &= ~(0xFF|SEND);
return SCPE_OK;
}
if (uptr->CMD & ENAB) {
if ((uptr->CMD & SEND) == 0) {
sim_debug(DEBUG_DETAIL, dptr, "unit=%d send write %x\n", unit, ch);
tmxr_putc_ln( &scom_ldsc[unit], ch);
uptr->CMD |= SEND;
}
if (scom_ldsc[unit].conn == 0) {
sim_debug(DEBUG_DETAIL, dptr, "unit=%d disco\n", unit);
uptr->CMD &= ~(0xff|INPUT|ENAB|RECV|INIT1|SEND|DATA);
uptr->SNS = SNS_CTLCHK;
uptr->BPTR = 0;
uptr->IPTR = 0;
chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP);
return SCPE_OK;
}
if (chan_read_byte (addr, &ch)) {
tmxr_putc_ln( &scom_ldsc[unit], IAC);
tmxr_putc_ln( &scom_ldsc[unit], EOR);
uptr->CMD &= ~(0xff| SEND);
sim_debug(DEBUG_CMD, dptr, "COM: unit=%d eor\n", unit);
chan_end(addr, SNS_CHNEND|SNS_DEVEND);
} else {
int32 data;
data = ebcdic_to_ascii[ch];
sim_debug(DEBUG_CMD, dptr, "COM: unit=%d send %02x '%c'\n",
unit, ch, isprint(data)? data: '^');
tmxr_putc_ln( &scom_ldsc[unit], ch);
if (ch == IAC)
tmxr_putc_ln( &scom_ldsc[unit], ch);
sim_activate(uptr, 200);
}
}
break;
case CMD_NOP: /* Nop scommand */
uptr->CMD &= ~0xff;
chan_end(addr, SNS_CHNEND|SNS_DEVEND);
break;
case CMD_SEL: /* Select */
uptr->CMD &= ~0xff;
uptr->SNS = 0;
sim_debug(DEBUG_CMD, dptr, "COM: unit=%d select done\n", unit);
chan_end(addr, SNS_CHNEND|SNS_DEVEND);
break;
}
return SCPE_OK;
}
/* Scan for new connections, flush and poll for data */
t_stat scom_scan(UNIT * uptr)
{
UNIT *line;
int32 ln;
struct _line *data;
int i;
sim_activate(uptr, tmxr_poll); /* continue poll */
if ((uptr->flags & UNIT_ATT) == 0) /* attached? */
return SCPE_OK;
ln = tmxr_poll_conn (&scom_desc); /* look for connect */
sim_debug(DEBUG_EXP, &scom_dev, "SCOM Poll %d\n", ln);
if (ln >= 0) { /* got one? rcv enb*/
line = &scoml_unit[ln];
data = (struct _line *)(line->up7);
sim_debug(DEBUG_DETAIL, &scom_dev, "SCOM line connect %d\n", ln);
scom_ldsc[ln].rcve = 1; /* Mark as ok */
for (i = 0; i < 256; i++)
data->option_state[i] = 0;
scom_sendoption(line, ln, DO, OPTION_TERMINAL);
scom_sendoption(line, ln, DO, OPTION_EOR);
line->CMD |= ENAB|DATA|INIT1;
line->CMD &= ~(RECV|SEND);
line->SNS = 0;
sim_activate(line, 20000);
}
/* See if a line is disconnected with no scommand on it. */
for (ln = 0; ln < scom_desc.lines; ln++) {
line = &scoml_unit[ln];
if ((line->CMD & (SEND|RECV|ENAB)) == ENAB && tmxr_rqln(&scom_ldsc[ln]) > 0) {
if ((line->CMD & (DATA|INIT1)) != 0 || (line->CMD & 0xff) != 0)
sim_activate(line, 200);
else
set_devattn(GET_UADDR(line->CMD), SNS_ATTN);
}
}
tmxr_poll_tx(&scom_desc);
tmxr_poll_rx(&scom_desc);
return SCPE_OK;
}
/* Process characters from remote */
t_stat
scom_readinput(UNIT *uptr)
{
DEVICE *dptr = find_dev_from_unit(uptr);
uint16 addr = GET_UADDR(uptr->CMD);
int unit = (uptr - dptr->units);
int32 r;
struct _line *data = (struct _line *)(uptr->up7);
uint8 ch;
while (((r = tmxr_getc_ln (&scom_ldsc[unit])) & TMXR_VALID) != 0) {
ch = r & 0xff;
sim_debug(DEBUG_DETAIL, dptr, "unit=%d got %x\n", unit, ch);
switch (data->state) {
case TS_DATA:
if (ch == IAC) {
data->state = TS_IAC;
break;
}
if (uptr->CMD & RECV) {
if (chan_write_byte( addr, &ch)) {
uptr->CMD &= ~(0xff|RECV);
chan_end(addr, SNS_CHNEND|SNS_DEVEND);
return SCPE_OK;
}
}
break;
case TS_IAC:
switch (ch) {
case WILL:
data->state = TS_WILL;
break;
case WONT:
data->state = TS_WONT;
break;
case DO:
data->state = TS_DO;
break;
case DONT:
data->state = TS_DONT;
break;
case IAC:
data->state = TS_DATA;
if (uptr->CMD & RECV) {
if (chan_write_byte( addr, &ch)) {
uptr->CMD &= ~(0xff|RECV);
chan_end(addr, SNS_CHNEND|SNS_DEVEND);
return SCPE_OK;
}
}
break;
case IP:
case BREAK:
case EOR:
data->state = TS_DATA;
if (uptr->CMD & RECV) {
uptr->CMD &= ~(0xff|RECV);
chan_end(addr, SNS_CHNEND|SNS_DEVEND);
}
break;
}
break;
case TS_WILL:
switch (ch) {
case OPTION_TERMINAL: /* Ignore terminal option */
data->option_state[ch] |= TC_WILL|TC_DONT;
break;
case OPTION_BINARY:
case OPTION_ECHO:
case OPTION_SGA:
case OPTION_EOR:
if ((data->option_state[ch] & TC_WILL) == 0) {
scom_sendoption(uptr, unit, WILL, ch);
if (ch == OPTION_EOR && (uptr->CMD & INIT1) != 0) {
scom_sendoption(uptr, unit, DO, OPTION_BINARY);
}
}
break;
default:
if ((data->option_state[ch] & TC_DONT) == 0)
scom_sendoption(uptr, unit, DONT, ch);
break;
}
data->state = TS_DATA;
break;
case TS_WONT:
if ((data->option_state[ch] & TC_WONT) == 0)
scom_sendoption(uptr, unit, WONT, ch);
break;
case TS_DO:
switch (ch) {
case OPTION_BINARY:
case OPTION_ECHO:
case OPTION_SGA:
case OPTION_EOR:
if ((data->option_state[ch] & TC_WILL) != 0) {
if (ch == OPTION_BINARY) {
uptr->CMD &= ~(DATA|INIT1);
uptr->CMD |= ENAB;
tmxr_putc_ln( &scom_ldsc[unit], REMOTE_EW);
tmxr_putc_ln( &scom_ldsc[unit], 0xc1);
tmxr_putc_ln( &scom_ldsc[unit], IAC);
tmxr_putc_ln( &scom_ldsc[unit], EOR);
if ((uptr->CMD & 0xff) == 0)
set_devattn(addr, SNS_ATTN);
else
sim_activate(uptr, 200);
}
}
if ((data->option_state[ch] & TC_DO) == 0)
scom_sendoption(uptr, unit, DO, ch);
break;
default:
if ((data->option_state[ch] & TC_WONT) == 0)
scom_sendoption(uptr, unit, WONT, ch);
break;
}
data->state = TS_DATA;
break;
case TS_DONT:
if ((data->option_state[ch] & TC_WILL) != 0) {
/* send IAC WONT option */
data->option_state[ch] &= ~TC_WILL;
scom_sendoption(uptr, unit, WILL, ch);
}
data->state = TS_DATA;
break;
}
}
return SCPE_OK;
}
void
scom_sendoption(UNIT *uptr, int unit, uint8 state, uint8 opt)
{
struct _line *data = (struct _line *)(uptr->up7);
tmxr_putc_ln( &scom_ldsc[unit], IAC);
tmxr_putc_ln( &scom_ldsc[unit], state);
tmxr_putc_ln( &scom_ldsc[unit], opt);
tmxr_send_buffered_data(&scom_ldsc[unit]);
switch(state) {
case WILL:
data->option_state[opt] |= TC_WILL;
break;
case WONT:
data->option_state[opt] |= TC_WONT;
break;
case DO:
data->option_state[opt] |= TC_DO;
break;
case DONT:
data->option_state[opt] |= TC_DONT;
}
}
t_stat
scom_reset(DEVICE * dptr)
{
int i;
sim_activate(&scom_unit[0], tmxr_poll);
(void)tmxr_set_notelnet (&scom_desc);
for (i = 0; i < NUM_UNITS_SCOM; i++)
scoml_unit[i].up7 = &line_data[i];
return SCPE_OK;
}
t_stat
scom_attach(UNIT * uptr, CONST char *cptr)
{
t_stat r;
int i;
if ((r = tmxr_attach(&scom_desc, uptr, cptr)) != SCPE_OK)
return r;
for (i = 0; i< scom_desc.lines; i++) {
scoml_unit[i].CMD &= ~0xffff;
}
sim_activate(uptr, tmxr_poll);
return SCPE_OK;
}
t_stat
scom_detach(UNIT * uptr)
{
t_stat r;
int i;
for (i = 0; i< scom_desc.lines; i++) {
(void)tmxr_set_get_modem_bits(&scom_ldsc[i], 0, TMXR_MDM_DTR, NULL);
(void)tmxr_reset_ln(&scom_ldsc[i]);
scoml_unit[i].CMD &= ~0xffff;
}
sim_cancel(uptr);
r = tmxr_detach(&scom_desc, uptr);
return r;
}
t_stat scom_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
const char *cptr)
{
fprint_set_help (st, dptr);
fprint_show_help (st, dptr);
return SCPE_OK;
}
const char *scom_description (DEVICE *dptr)
{
return "IBM 3271 communications controller";
}
#endif