/* i7000_chan.c: IBM 7000 Channel simulator | |
Copyright (c) 2005-2016, 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. | |
channel | |
Common routines for handling channel functions. | |
*/ | |
#include "i7000_defs.h" | |
extern DEVICE *sim_devices[]; | |
int num_devs[NUM_CHAN]; | |
t_stat | |
chan_set_devs(DEVICE * dptr) | |
{ | |
int i; | |
for(i = 0; i < NUM_CHAN; i++) { | |
num_devs[i] = 0; | |
} | |
/* Build channel array */ | |
for (i = 0; sim_devices[i] != NULL; i++) { | |
UNIT *uptr = sim_devices[i]->units; | |
DIB *dibp = (DIB *) sim_devices[i]->ctxt; | |
int ctype; | |
int num; | |
/* If no DIB, not channel device */ | |
if (dibp == NULL) | |
continue; | |
/* Skip channel devices */ | |
if (sim_devices[i] == &chan_dev) | |
continue; | |
/* Skip disabled devices */ | |
if (sim_devices[i]->flags & DEV_DIS) | |
continue; | |
ctype = dibp->ctype; | |
if (dibp->upc > 1) { | |
int chan = UNIT_G_CHAN(uptr->flags); | |
int type = CHAN_G_TYPE(chan_unit[chan].flags); | |
if (((1 << type) & ctype) == 0) { | |
if ((chan_unit[chan].flags & CHAN_SET) || | |
((chan_unit[chan].flags & CHAN_AUTO) | |
&& num_devs[chan] != 0)) { | |
for (num = sim_devices[i]->numunits; num > 0; num--) | |
(uptr++)->flags |= UNIT_DIS; | |
goto nextdev; | |
} | |
} | |
/* Set channel to highest type */ | |
if ((chan_unit[chan].flags & CHAN_SET) == 0) { | |
/* Set type to highest found */ | |
for(type = 7; type >=0; type--) | |
if (ctype & (1 << type)) | |
break; | |
chan_unit[chan].flags &= ~(CHAN_MODEL); | |
chan_unit[chan].flags |= CHAN_S_TYPE(type)|CHAN_SET; | |
} | |
num_devs[chan] += sim_devices[i]->numunits; | |
if (dibp->ini != NULL) { | |
for (num = sim_devices[i]->numunits; num > 0; num--) { | |
uptr->flags &= ~UNIT_CHAN; | |
uptr->flags |= UNIT_S_CHAN(chan); | |
dibp->ini(uptr++, 1); | |
} | |
} | |
goto nextdev; | |
} | |
for (num = sim_devices[i]->numunits; num > 0; num--) { | |
int chan = UNIT_G_CHAN(uptr->flags); | |
int type = CHAN_G_TYPE(chan_unit[chan].flags); | |
if ((uptr->flags & UNIT_DIS) == 0) { | |
if (((1 << type) & ctype) == 0) { | |
if ((chan_unit[chan].flags & CHAN_SET) || | |
((chan_unit[chan].flags & CHAN_AUTO) | |
&& num_devs[chan] != 0)) { | |
uptr->flags |= UNIT_DIS; | |
goto next; | |
} | |
} | |
/* Set channel to highest type */ | |
if ((chan_unit[chan].flags & CHAN_SET) == 0) { | |
/* Set type to highest found */ | |
for(type = 7; type >=0; type--) | |
if (ctype & (1 << type)) | |
break; | |
chan_unit[chan].flags &= ~(CHAN_MODEL); | |
chan_unit[chan].flags |= CHAN_S_TYPE(type)|CHAN_SET; | |
} | |
num_devs[chan]++; | |
if (dibp->ini != NULL) | |
dibp->ini(uptr, 1); | |
} | |
next: | |
uptr++; | |
} | |
nextdev: | |
; | |
} | |
return SCPE_OK; | |
} | |
/* Print help for "SET dev CHAN" based on allowed types */ | |
void help_set_chan_type(FILE *st, DEVICE *dptr, const char *name) | |
{ | |
#if NUM_CHAN > 1 | |
DIB *dibp = (DIB *) dptr->ctxt; | |
int ctype = dibp->ctype; | |
int i; | |
int m; | |
fprintf (st, "Devices can be moved to any channel via the command\n\n"); | |
fprintf (st, " sim> SET %s CHAN=x where x is", dptr->name); | |
if (ctype & 3) { | |
if (ctype == 1 || ctype == 2) | |
fprintf(st, " only"); | |
fprintf (st, " %s", chname[0]); | |
if ((ctype & ~3) != 0) | |
fprintf(st, " or"); | |
} | |
if ((ctype & ~3) != 0) | |
fprintf(st, " %s to %s", chname[1], chname[NUM_CHAN-1]); | |
fprintf (st, "\n\n%s can be attached to ", name); | |
m = 1; | |
for(i = 0; ctype != 0; i++) { | |
if (ctype & m) { | |
fprintf(st, "%s", chan_type_name[i]); | |
ctype &= ~m; | |
if (ctype != 0) | |
fprintf(st, ", or "); | |
} | |
m <<= 1; | |
} | |
fprintf(st, " channel\n"); | |
#endif | |
} | |
/* Sets the device onto a given channel */ | |
t_stat | |
set_chan(UNIT * uptr, int32 val, CONST char *cptr, void *desc) | |
{ | |
DEVICE *dptr; | |
DIB *dibp; | |
int newch; | |
int chan; | |
int num; | |
int type; | |
int ctype; | |
int compat; | |
if (cptr == NULL) | |
return SCPE_ARG; | |
if (uptr == NULL) | |
return SCPE_IERR; | |
dptr = find_dev_from_unit(uptr); | |
if (dptr == NULL) | |
return SCPE_IERR; | |
chan = UNIT_G_CHAN(uptr->flags); | |
dibp = (DIB *) dptr->ctxt; | |
if (dibp == NULL) | |
return SCPE_IERR; | |
for(newch = 0; newch < NUM_CHAN; newch++) | |
if (strcmp(cptr, chname[newch]) == 0) | |
break; | |
if (newch == NUM_CHAN) | |
return SCPE_ARG; | |
if (newch == chan) | |
return SCPE_OK; | |
ctype = dibp->ctype; | |
compat = ctype; | |
/* Update the number of devices on this channel */ | |
num_devs[newch] = 0; | |
for (num = 0; sim_devices[num] != NULL; num++) { | |
UNIT *u = sim_devices[num]->units; | |
DIB *dibp = (DIB *) sim_devices[num]->ctxt; | |
int units = sim_devices[num]->numunits; | |
/* If no DIB, not channel device */ | |
if (dibp == NULL) | |
continue; | |
/* Skip channel devices */ | |
if (sim_devices[num] == &chan_dev) | |
continue; | |
/* Skip disabled devices */ | |
if (sim_devices[num]->flags & DEV_DIS) | |
continue; | |
if (dibp->upc > 1) { | |
if ((u->flags & UNIT_DIS) == 0 && | |
UNIT_G_CHAN(u->flags) == chan) { | |
num_devs[newch] += units; | |
compat &= dibp->ctype; | |
} | |
} else { | |
int i; | |
for (i = 0; i < units; i++) { | |
if ((u->flags & UNIT_DIS) == 0 && | |
UNIT_G_CHAN(u->flags) == chan) { | |
num_devs[newch]++; | |
compat &= dibp->ctype; | |
} | |
u++; | |
} | |
} | |
} | |
/* If nothing left on channel, drop set bit */ | |
if (num_devs[newch] == 0 && chan_unit[newch].flags & CHAN_AUTO) { | |
chan_unit[newch].flags &= ~CHAN_SET; | |
compat = ctype; | |
} | |
/* Check if same type or everyone can handle new type */ | |
type = CHAN_G_TYPE(chan_unit[newch].flags); | |
if (((1 << type) & ctype) == 0) { | |
/* If set or no common types */ | |
if (chan_unit[newch].flags & CHAN_SET && compat == 0) | |
return SCPE_IERR; | |
if ((chan_unit[newch].flags & CHAN_AUTO) && | |
(compat == 0 && num_devs[newch] != 0)) | |
return SCPE_IERR; | |
else { | |
/* Set type to highest compatable type */ | |
for(type = 7; type >=0; type--) | |
if (compat >> type) | |
break; | |
chan_unit[newch].flags &= ~(CHAN_MODEL); | |
chan_unit[newch].flags |= CHAN_S_TYPE(type)|CHAN_SET; | |
} | |
} | |
/* Set channel to highest type */ | |
if ((chan_unit[chan].flags & CHAN_SET) == 0) { | |
/* Set type to highest found */ | |
for(type = 7; type >=0; type--) | |
if (ctype >> type) | |
break; | |
chan_unit[chan].flags &= ~(CHAN_MODEL); | |
chan_unit[chan].flags |= CHAN_S_TYPE(type)|CHAN_SET; | |
} | |
/* Detach unit from orignal channel */ | |
if (dibp->upc > 1) | |
num_devs[chan] -= dptr->numunits; | |
else | |
num_devs[chan]--; | |
if (num_devs[chan] == 0 && (chan_unit[chan].flags & CHAN_AUTO)) | |
chan_unit[chan].flags &= ~CHAN_SET; | |
/* Hook up to new channel */ | |
if (dibp->upc > 1) { | |
uint32 unit; | |
for (unit = 0; unit < dptr->numunits; unit++) { | |
/* Set the new channel */ | |
dptr->units[unit].flags &= ~UNIT_CHAN; | |
dptr->units[unit].flags |= UNIT_S_CHAN(newch); | |
} | |
num_devs[newch] += dptr->numunits; | |
} else { | |
/* Set the new channel */ | |
uptr->flags &= ~UNIT_CHAN; | |
uptr->flags |= UNIT_S_CHAN(newch); | |
num_devs[newch]++; | |
} | |
return SCPE_OK; | |
} | |
/* Print devices on channel */ | |
t_stat | |
print_chan(FILE * st, UNIT * uptr, int32 v, CONST void *desc) | |
{ | |
int chan = uptr - chan_unit; | |
int i; | |
/* Check all devices */ | |
fprintf(st, "units="); | |
for (i = 0; sim_devices[i] != NULL; i++) { | |
UNIT *u = sim_devices[i]->units; | |
DIB *dibp = (DIB *) sim_devices[i]->ctxt; | |
uint32 num; | |
/* If no DIB, not channel device */ | |
if (dibp == NULL) | |
continue; | |
/* Skip channel devices */ | |
if (sim_devices[i] == &chan_dev) | |
continue; | |
/* Skip disabled devices */ | |
if (sim_devices[i]->flags & DEV_DIS) | |
continue; | |
if (dibp->upc > 1) { | |
if ((u->flags & UNIT_DIS) == 0 && | |
UNIT_G_CHAN(u->flags) == chan) | |
fprintf(st, "%s, ", sim_devices[i]->name); | |
} else { | |
for (num = 0; num < sim_devices[i]->numunits; num++) { | |
if ((u->flags & UNIT_DIS) == 0 && | |
UNIT_G_CHAN(u->flags) == chan) | |
fprintf(st, "%s%d, ", sim_devices[i]->name, num); | |
u++; | |
} | |
} | |
} | |
return SCPE_OK; | |
} | |
t_stat | |
get_chan(FILE * st, UNIT * uptr, int32 v, CONST void *desc) | |
{ | |
DEVICE *dptr; | |
DIB *dibp; | |
int chan; | |
if (uptr == NULL) | |
return SCPE_IERR; | |
chan = UNIT_G_CHAN(uptr->flags); | |
dptr = find_dev_from_unit(uptr); | |
if (dptr == NULL) | |
return SCPE_IERR; | |
dibp = (DIB *) dptr->ctxt; | |
if (dibp == NULL) | |
return SCPE_IERR; | |
fprintf(st, "Chan=%s", chname[chan]); | |
return SCPE_OK; | |
} | |
t_stat | |
chan9_set_select(UNIT * uptr, int32 val, CONST char *cptr, void *desc) | |
{ | |
int newsel; | |
DEVICE *dptr; | |
DIB *dibp; | |
if (cptr == NULL) | |
return SCPE_ARG; | |
if (uptr == NULL) | |
return SCPE_IERR; | |
if (*cptr == '\0' || cptr[1] != '\0') | |
return SCPE_ARG; | |
if (*cptr == '0') | |
newsel = 0; | |
else if (*cptr == '1') | |
newsel = 1; | |
else | |
return SCPE_ARG; | |
dptr = find_dev_from_unit(uptr); | |
if (dptr == NULL) | |
return SCPE_IERR; | |
dibp = (DIB *) dptr->ctxt; | |
if (dibp == NULL) | |
return SCPE_IERR; | |
/* Change to new selection. */ | |
if (dibp->upc > 1) { | |
uint32 unit; | |
for (unit = 0; unit < dptr->numunits; unit++) { | |
if (newsel) | |
dptr->units[unit].flags |= UNIT_SELECT; | |
else | |
dptr->units[unit].flags &= ~UNIT_SELECT; | |
} | |
} else { | |
if (newsel) | |
uptr->flags |= UNIT_SELECT; | |
else | |
uptr->flags &= ~UNIT_SELECT; | |
} | |
return SCPE_OK; | |
} | |
t_stat | |
chan9_get_select(FILE * st, UNIT * uptr, int32 v, CONST void *desc) | |
{ | |
if (uptr == NULL) | |
return SCPE_IERR; | |
if (uptr->flags & UNIT_SELECT) | |
fputs("Select=1", st); | |
else | |
fputs("Select=0", st); | |
return SCPE_OK; | |
} | |
/* Check channel for error */ | |
int chan_error(int chan) | |
{ | |
return chan_flags[chan] & CHS_ATTN; | |
} | |
/* Check channel for flag, clear it if it was set */ | |
int chan_stat(int chan, uint32 flag) | |
{ | |
if (chan_flags[chan] & flag) { | |
chan_flags[chan] &= ~flag; | |
return 1; | |
} | |
return 0; | |
} | |
/* Check channel for flag */ | |
int chan_test(int chan, uint32 flag) | |
{ | |
if (chan_flags[chan] & flag) | |
return 1; | |
return 0; | |
} | |
/* Check channel is selected */ | |
int chan_select(int chan) | |
{ | |
return chan_flags[chan] & DEV_SEL; | |
} | |
/* Check channel is active */ | |
int chan_active(int chan) | |
{ | |
return (chan_flags[chan] & | |
(DEV_DISCO |DEV_SEL | STA_ACTIVE | STA_WAIT | STA_TWAIT)) != 0; | |
} | |
void | |
chan_set_attn(int chan) | |
{ | |
chan_flags[chan] |= CHS_ATTN; | |
} | |
void | |
chan_set_eof(int chan) | |
{ | |
chan_flags[chan] |= CHS_EOF; | |
} | |
void | |
chan_set_error(int chan) | |
{ | |
chan_flags[chan] |= CHS_ERR; | |
} | |
void | |
chan_set_sel(int chan, int need) | |
{ | |
chan_flags[chan] &= | |
~(DEV_WEOR | DEV_REOR | DEV_FULL | DEV_WRITE | DEV_DISCO); | |
chan_flags[chan] |= DEV_SEL; | |
if (need) | |
chan_flags[chan] |= DEV_WRITE; | |
} | |
void | |
chan_clear_status(int chan) | |
{ | |
chan_flags[chan] &= | |
~(CHS_ATTN | CHS_EOT | CHS_BOT | DEV_REOR | DEV_WEOR); | |
} | |
void | |
chan_set(int chan, uint32 flag) | |
{ | |
chan_flags[chan] |= flag; | |
} | |
void | |
chan_clear(int chan, uint32 flag) | |
{ | |
chan_flags[chan] &= ~flag; | |
} | |
void | |
chan9_clear_error(int chan, int sel) { | |
chan_flags[chan] &= ~(SNS_UEND | (SNS_ATTN1 >> sel)); | |
} | |
void | |
chan9_set_attn(int chan, int sel) | |
{ | |
uint16 mask = SNS_ATTN1 >> sel; | |
chan9_set_error(chan, mask); | |
} | |