1
0
mirror of https://github.com/rcornwell/sims.git synced 2026-01-13 15:27:04 +00:00
rcornwell.sims/ICL1900/icl1900_stdio.c
2020-10-12 23:27:46 -04:00

487 lines
14 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.

/* icl1900_stdio.c: ICL 1900 standard I/O devices
Copyright (c) 2018, 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 "icl1900_defs.h"
/* Define style bits */
#define OUTPUT B0 /* Input/Output flag */
#define BACKWARD B1 /* Forward/Backword */
#define WORDCCW B2 /* Character/Word */
#define MULTICHN B3 /* Single/Multi Channel */
#define CWRECHARGE B4 /* Recharge CW when empty */
#define GATHER B5 /* Gather */
DIB *devs[64]; /* Pointer to device DIB entry */
DIB nul_dib = { 0, NULL, NULL, NULL};
t_stat
chan_set_devs()
{
unsigned int i;
uint32 j;
int chan;
/* Clear device table */
for (i = 0; i < sizeof(devs)/sizeof(DIB *); i++)
devs[i] = NULL;
/* Add in hidden channels */
devs[22] = &nul_dib;
devs[23] = &nul_dib;
/* Scan all devices and assign to channel */
for(i = 0; sim_devices[i] != NULL; i++) {
DIB *dibp = (DIB *) sim_devices[i]->ctxt;
/* Check if device a channel device */
if (dibp == NULL)
continue;
/* If device is disabled, don't add it */
if (sim_devices[i]->flags & DEV_DIS)
continue;
if (dibp->type & BLK_DEV) {
int f = 1;
chan = GET_UADDR(sim_devices[i]->flags);
/* Make sure it is in range */
if (chan < 2 || (chan + sim_devices[i]->numunits) > 36)
continue;
for (j = 0; j < sim_devices[i]->numunits; j++) {
if (sim_devices[i]->units[j].flags & UNIT_DIS)
continue;
if (devs[chan+j] != NULL) {
sim_printf("Conflict between devices %d %s\n", chan+j, sim_devices[i]->name);
f = 0;
}
}
if (f) {
for (j = 0; j < sim_devices[i]->numunits; j++) {
if (sim_devices[i]->units[j].flags & UNIT_DIS)
continue;
devs[chan+j] = dibp;
}
}
} else if (dibp->type & MULT_DEV) {
chan = GET_UADDR(sim_devices[i]->flags);
/* Make sure it is in range */
if (chan < 2 || chan > 36)
continue;
if (devs[chan] != NULL) {
sim_printf("Conflict between devices %d %s\n", chan, sim_devices[i]->name);
} else {
devs[chan] = dibp;
}
} else {
UNIT *uptr = sim_devices[i]->units;
uint32 unit;
for (unit = 0; unit < sim_devices[i]->numunits; unit++, uptr++) {
/* If disabled, skip it */
if (uptr->flags & UNIT_DIS)
continue;
chan = GET_UADDR(uptr->flags);
/* Make sure it is in range */
if (chan < 2 || chan > 36)
continue;
if (devs[chan] != NULL) {
sim_printf("Conflict between devices %d %s%d\n", chan, sim_devices[i]->name, unit);
} else {
devs[chan] = dibp;
}
}
}
}
return SCPE_OK;
}
/* Set the device onto a given channel */
t_stat
set_chan(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
DEVICE *dptr;
DIB *dibp;
int new_chan;
int cur_chan;
t_stat r;
uint32 i;
if (cptr == NULL)
return SCPE_ARG;
if (uptr == NULL)
return SCPE_IERR;
dptr = find_dev_from_unit(uptr);
if (dptr == NULL)
return SCPE_IERR;
dibp = (DIB *) dptr->ctxt;
if (dibp == NULL)
return SCPE_IERR;
new_chan = get_uint(cptr, 10, 37, &r);
if (r != SCPE_OK)
return r;
if (new_chan < 4) /* Lowest channel is 4 */
return SCPE_ARG;
/* Find channel device is current on and remove it */
if (dibp->type & BLK_DEV) {
if (new_chan < 2 || (new_chan + dptr->numunits) > 36)
return SCPE_ARG;
/* Validate that we can actually set this device. */
for (i = 0; i < dptr->numunits; i++) {
if (dptr->units[i].flags & UNIT_DIS)
continue;
if (devs[new_chan+i] != dibp && devs[new_chan+i] != NULL)
return SCPE_ARG;
}
/* Free up current device */
cur_chan = GET_UADDR(dptr->flags);
for (i = 0; i < dptr->numunits; i++) {
if (dptr->units[i].flags & UNIT_DIS)
continue;
if (devs[cur_chan+i] == dibp)
devs[cur_chan] = NULL;
}
} else if (dibp->type & MULT_DEV) {
cur_chan = GET_UADDR(dptr->flags);
} else {
cur_chan = GET_UADDR(uptr->flags);
}
if (dibp == devs[cur_chan])
devs[cur_chan] = NULL;
/* If device is disabled, set to whatever the user wants */
if (dptr->flags & DEV_DIS) {
if (dibp->type & (MULT_DEV|BLK_DEV)) {
dptr->flags &= ~UNIT_M_ADDR;
dptr->flags |= UNIT_ADDR(new_chan);
} else {
uptr->flags &= ~UNIT_M_ADDR;
uptr->flags |= UNIT_ADDR(new_chan);
}
return SCPE_OK;
}
if (dibp->type & BLK_DEV) {
dptr->flags &= ~UNIT_M_ADDR;
dptr->flags |= UNIT_ADDR(new_chan);
for (i = 0; i < dptr->numunits; i++) {
if (dptr->units[i].flags & UNIT_DIS)
continue;
devs[new_chan+i] = dibp;
}
return SCPE_OK;
}
if (devs[new_chan] == NULL) {
if (dibp->type & MULT_DEV) {
dptr->flags &= ~UNIT_M_ADDR;
dptr->flags |= UNIT_ADDR(new_chan);
} else {
uptr->flags &= ~UNIT_M_ADDR;
uptr->flags |= UNIT_ADDR(new_chan);
}
devs[new_chan] = dibp;
return SCPE_OK;
} else {
sim_printf("Device already on channel %d\n", new_chan);
}
return SCPE_ARG;
}
/* Print the channel the device is on */
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;
dptr = find_dev_from_unit(uptr);
if (dptr == NULL)
return SCPE_IERR;
dibp = (DIB *) dptr->ctxt;
if (dibp == NULL)
return SCPE_IERR;
if (dibp->type & BLK_DEV) {
chan = GET_UADDR(dptr->flags) + (uptr - dptr->units);
} else if (dibp->type & MULT_DEV) {
chan = GET_UADDR(dptr->flags);
} else {
chan = GET_UADDR(uptr->flags);
}
fprintf(st, "DEV=%d", chan);
return SCPE_OK;
}
int
get_ccw(int dev, uint32 *addr, uint8 type) {
int cw_addr;
uint32 cw0; /* Holding registers for current control word */
uint32 cw1;
if (io_flags & EXT_IO) {
cw_addr = 256+4*dev;
cw0 = M[cw_addr];
cw1 = M[cw_addr+1];
*addr = cw1;
if (type & WORD_DEV)
cw0 |= WORDCCW;
if (cw0 & WORDCCW) {
if (cw0 & BACKWARD) {
cw1 = ((cw1 + M22) & M22) | (cw1 & CMASK);
*addr = cw1;
} else
cw1 = ((cw1 + 1) & M22) | (cw1 & CMASK);
} else {
if (cw0 & BACKWARD) {
if (cw1 & CMASK) {
cw1 -= B1;
} else {
cw1 = ((cw1 - 1) & M22) | CMASK;
}
*addr = cw1;
} else {
if ((cw1 & CMASK) == CMASK) {
cw1 = (cw1 + 1) & M22;
} else {
cw1 += B1;
}
}
}
cw0 = ((cw0 - 1) & M15) | (cw0 & CNTMSK);
if ((cw0 & M15) == 0) {
if ((cw0 & (CWRECHARGE|GATHER)) == (CWRECHARGE)) {
cw0 = M[cw_addr+2];
cw1 = M[cw_addr+3];
} else if ((cw0 & GATHER) != 0) {
int a;
if ((cw0 & CWRECHARGE) != 0)
M[cw_addr+3] = M[cw_addr+2];
a = M[cw_addr+3];
cw0 = M[a & M22];
cw1 = M[(a + 1) & M22];
M[cw_addr+3] = ((a + 2) & M22) | (a & CMASK);
}
M[cw_addr] = cw0;
M[cw_addr+1] = cw1;
return (cw0 & M15) == 0;
}
M[cw_addr] = cw0;
M[cw_addr+1] = cw1;
} else {
cw_addr = 64+dev;
cw0 = M[cw_addr];
if (type & WORD_DEV) {
*addr = cw0 & M15;
cw0 = ((cw0 + 1) & M15) | ((cw0 + CNTMSK) & CNTMSK);
cw1 = cw0 & CNTMSK;
} else {
*addr = cw0 & (CMASK|M15);
if ((cw0 & CMASK) == CMASK) {
cw0 = ((cw0 + 1) & M15) | (cw0 & CHCMSK);
} else {
cw0 += B1;
}
cw1 = (cw0 + CHCMSK) & CHCMSK;
cw0 = ((CMASK|M15) & cw0) | cw1;
}
M[cw_addr] = cw0;
if (cw1 == 0) {
if (type & LONG_BLK) {
cw1 = (M[cw_addr + 64] - 1) & FMASK;
M[cw_addr + 64] = cw1;
}
if (type & SPEC_HES) {
M[cw_addr] = M[cw_addr+1];
return 0;
}
return (cw1 == 0);
}
}
return 0;
}
/* Talk to non-standard interface devices */
void
chan_nsi_cmd(int dev, uint32 cmd) {
DIB *dibp = devs[dev & 077];
if (dibp != NULL && dibp->nsi_cmd != NULL) {
(dibp->nsi_cmd)(dev, cmd);
}
}
/* Talk to non-standard interface devices */
void
chan_nsi_status(int dev, uint32 *resp) {
DIB *dibp = devs[dev & 077];
*resp = 0;
if (dibp != NULL && dibp->nsi_cmd != NULL) {
(dibp->nsi_status)(dev, resp);
}
}
/* Hessitation operations */
void
chan_send_cmd(int dev, uint32 cmd, uint32 *resp) {
DIB *dibp = devs[dev & 077];
*resp = 0;
if (dibp != NULL && dibp->si_cmd != NULL) {
(dibp->si_cmd)(dev, cmd, resp);
}
}
/* Transfer date between device and memory */
int
chan_input_char(int dev, uint8 *data, int eor) {
DIB *dibp = devs[dev & 077];
int r;
int c;
uint32 addr;
uint32 mb;
/* Make sure device assign here */
if (dibp == NULL)
return -1;
/* Check if right type */
if ((dibp->type & WORD_DEV) != 0)
return -2;
/* Get address of next character. */
r = get_ccw(dev, &addr, dibp->type);
c = (addr >> 22) & 3;
c = 6 * (3 - c);
addr &= M22;
if (addr < 8)
mb = XR[addr];
else
mb = M[addr];
mb &= ~(077 << c);
mb |= ((uint32)(*data) & 077) << c;
if (addr < 8)
XR[addr] = mb;
M[addr] = mb;
return r;
}
int
chan_output_char(int dev, uint8 *data, int eor) {
DIB *dibp = devs[dev & 077];
int r;
int c;
uint32 addr;
uint32 mb;
/* Make sure device assigne here */
if (dibp == NULL)
return -1;
/* Check if right type */
if ((dibp->type & WORD_DEV) != 0)
return -2;
/* Get address of next character. */
r = get_ccw(dev, &addr, dibp->type);
c = (addr >> 22) & 3;
c = 6 * (3 - c);
addr &= M22;
if (addr < 8)
mb = XR[addr];
else
mb = M[addr];
*data = (uint8)(mb >> c) & 077;
return r;
}
int
chan_input_word(int dev, uint32 *data, int eor) {
DIB *dibp = devs[dev & 077];
int r;
uint32 addr;
/* Make sure device assigne here */
if (dibp == NULL)
return -1;
/* Check if right type */
if ((dibp->type & WORD_DEV) == 0)
return -2;
/* Get address of next word. */
r = get_ccw(dev, &addr, dibp->type);
addr &= M22;
if (addr < 8)
XR[addr] = *data;
M[addr] = *data;
return r;
}
int
chan_output_word(int dev, uint32 *data, int eor) {
DIB *dibp = devs[dev & 077];
int r;
uint32 addr;
/* Make sure device assigne here */
if (dibp == NULL)
return -1;
/* Check if right type */
if ((dibp->type & WORD_DEV) == 0)
return -2;
/* Get address of next word. */
r = get_ccw(dev, &addr, dibp->type);
addr &= M22;
if (addr < 8)
*data = XR[addr];
else
*data = M[addr];
return r;
}
void
chan_set_done(int dev) {
if (dev < 24)
SR64 |= B2 >> dev;
else
SR65 |= ((io_flags & EXT_IO) ? B1 : B2) >> (dev - 24);
}
void
chan_clr_done(int dev) {
if (dev < 24)
SR64 &= ~(B2 >> dev);
else
SR65 &= ~(((io_flags & EXT_IO) ? B1 : B2) >> (dev - 24));
}