1
0
mirror of https://github.com/rcornwell/sims.git synced 2026-04-25 19:51:57 +00:00
Files
rcornwell.sims/IBM360/ibm360_chan.c
2019-07-03 18:31:40 -04:00

1080 lines
35 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_chan.c: IBM 360 Channel functions.
Copyright (c) 2017, 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_defs.h"
#define CCMDMSK 0xff000000 /* Mask for command */
#define CDADRMSK 0x00ffffff /* Mask for data address */
#define CCNTMSK 0x0000ffff /* Mask for data count */
#define CD 0x80000000 /* Chain data */
#define CC 0x40000000 /* Chain command */
#define SLI 0x20000000 /* Suppress length indication */
#define SKIP 0x10000000 /* Skip flag */
#define PCI 0x08000000 /* Program controlled interuption */
/* Command masks */
#define CMD_TYPE 0x3 /* Type mask */
#define CMD_CHAN 0x0 /* Channel command */
#define CMD_WRITE 0x1 /* Write command */
#define CMD_READ 0x2 /* Read command */
#define CMD_CTL 0x3 /* Control command */
#define CMD_SENSE 0x4 /* Sense channel command */
#define CMD_TIC 0x8 /* Transfer in channel */
#define CMD_RDBWD 0xc /* Read backward */
#define STATUS_ATTN 0x8000 /* Device raised attention */
#define STATUS_MOD 0x4000 /* Status modifier */
#define STATUS_CTLEND 0x2000 /* Control end */
#define STATUS_BUSY 0x1000 /* Device busy */
#define STATUS_CEND 0x0800 /* Channel end */
#define STATUS_DEND 0x0400 /* Device end */
#define STATUS_CHECK 0x0200 /* Unit check */
#define STATUS_EXPT 0x0100 /* Unit excpetion */
#define STATUS_PCI 0x0080 /* Program interupt */
#define STATUS_LENGTH 0x0040 /* Incorrect lenght */
#define STATUS_PCHK 0x0020 /* Program check */
#define STATUS_PROT 0x0010 /* Protection check */
#define STATUS_CDATA 0x0008 /* Channel data check */
#define STATUS_CCNTL 0x0004 /* Channel control check */
#define STATUS_INTER 0x0002 /* Channel interface check */
#define STATUS_CHAIN 0x0001 /* Channel chain check */
#define FLAG_CD 0x8000 /* Chain data */
#define FLAG_CC 0x4000 /* Chain command */
#define FLAG_SLI 0x2000 /* Suppress length indicator */
#define FLAG_SKIP 0x1000 /* Suppress memory write */
#define FLAG_PCI 0x0800 /* Program controled interrupt */
#define BUFF_EMPTY 0x4 /* Buffer is empty */
#define BUFF_DIRTY 0x8 /* Buffer is dirty flag */
#define BUFF_NEWCMD 0x10 /* Channel ready for new command */
#define BUFF_CHNEND 0x20 /* Channel end */
#define AMASK 0x00ffffff
#define PMASK 0xf0000000 /* Storage protection mask */
extern uint32 *M;
extern uint8 key[MAXMEMSIZE / 2048];
#define MAX_DEV (MAX_CHAN * 256)
int channels = MAX_CHAN;
int subchannels = SUB_CHANS; /* Number of subchannels */
int irq_pend = 0;
uint32 caw[256]; /* Channel command address word */
uint32 ccw_addr[256]; /* Channel address */
uint16 ccw_count[256]; /* Channel count */
uint8 ccw_cmd[256]; /* Channel command and flags */
uint16 ccw_flags[256]; /* Channel flags */
uint16 chan_status[256]; /* Channel status */
uint16 chan_dev[256]; /* Device on channel */
uint32 chan_buf[256]; /* Channel data buffer */
uint8 chan_byte[256]; /* Current byte, dirty/full */
DIB *dev_unit[MAX_DEV]; /* Pointer to Device info block */
uint8 dev_status[MAX_DEV]; /* last device status flags */
/* Find unit pointer for given device */
UNIT *
find_chan_dev(uint16 addr) {
struct dib *dibp;
UNIT *uptr;
int i;
dibp = dev_unit[addr];
if (dibp == 0)
return NULL;
uptr = dibp->units;
if (dibp->mask == 0) {
for (i = 0; i < dibp->numunits; i++) {
if (addr == GET_UADDR(uptr->u3))
return uptr;
uptr++;
}
} else {
return uptr + (addr & ~dibp->mask & 0xff);
}
return NULL;
}
/* channel:
subchannels = 128
0 - 7 0x80-0xff
8 - 127 0x00-0x7f
128 - +6 0x1xx - 0x6xx
*/
/* look up device to find subchannel device is on */
int
find_subchan(uint16 device) {
int chan;
if (device > MAX_DEV)
return -1;
if (device > 0xff) {
chan = (device >> 8) & 0x7;
if (chan > channels)
return -1;
return subchannels + chan;
}
if (device < subchannels)
return device;
return ((device - subchannels)>>4) & 0xf;
}
/* Read a full word into memory.
* Return 1 if fail.
* Return 0 if success.
*/
int
readfull(int chan, uint32 addr, uint32 *word) {
int sk, k;
if ((addr & AMASK) > MEMSIZE) {
chan_status[chan] |= STATUS_PCHK;
irq_pend = 1;
return 1;
}
sk = (addr >> 24) & 0xf0;
addr &= AMASK;
addr >>= 2;
if (sk != 0) {
if ((cpu_unit.flags & FEAT_PROT) == 0) {
chan_status[chan] |= STATUS_PROT;
irq_pend = 1;
return 1;
}
k = key[addr >> 9];
if ((k & 0x8) != 0 && (k & 0xf0) != sk) {
//fprintf(stderr, "Prot %08x %x %x\n\r", addr << 2, k, sk);
chan_status[chan] |= STATUS_PROT;
irq_pend = 1;
return 1;
}
}
*word = M[addr];
return 0;
}
/* Read a word into the channel buffer.
* Return 1 if fail.
* Return 0 if success.
*/
int
readbuff(int chan) {
int sk, k;
uint32 addr = ccw_addr[chan];
if ((addr & AMASK) > MEMSIZE) {
chan_status[chan] |= STATUS_PCHK;
chan_byte[chan] = BUFF_CHNEND;
irq_pend = 1;
return 1;
}
sk = (addr >> 24) & 0xf0;
addr &= AMASK;
addr >>= 2;
if (sk != 0) {
if ((cpu_unit.flags & FEAT_PROT) == 0) {
chan_status[chan] |= STATUS_PROT;
chan_byte[chan] = BUFF_CHNEND;
irq_pend = 1;
return 1;
}
k = key[addr >> 9];
if ((k & 0x8) != 0 && (k & 0xf0) != sk) {
//fprintf(stderr, "Prot %08x %x %x\n\r", addr << 2, k, sk);
chan_status[chan] |= STATUS_PROT;
chan_byte[chan] = BUFF_CHNEND;
irq_pend = 1;
return 1;
}
}
chan_buf[chan] = M[addr];
if ((ccw_cmd[chan] & CMD_TYPE) == CMD_WRITE) {
sim_debug(DEBUG_CDATA, &cpu_dev, "Channel write %02x %06x %08x %08x '",
chan, ccw_addr[chan] & 0xFFFFFC, chan_buf[chan], ccw_count[chan]);
for(k = 24; k >= 0; k -= 8) {
unsigned char ch = ebcdic_to_ascii[(chan_buf[chan] >> k) & 0xFF];
if (ch < 0x20 || ch == 0xff)
ch = '.';
sim_debug(DEBUG_CDATA, &cpu_dev, "%c", ch);
}
sim_debug(DEBUG_CDATA, & cpu_dev, "'\n");
}
return 0;
}
/* Write channel buffer to memory.
* Return 1 if fail.
* Return 0 if success.
*/
int
writebuff(int chan) {
int sk, k;
uint32 addr = ccw_addr[chan];
if ((addr & AMASK) > MEMSIZE) {
chan_status[chan] |= STATUS_PCHK;
chan_byte[chan] = BUFF_CHNEND;
irq_pend = 1;
return 1;
}
sk = (addr >> 24) & 0xff;
addr &= AMASK;
addr >>= 2;
if (sk != 0) {
if ((cpu_unit.flags & FEAT_PROT) == 0) {
chan_status[chan] |= STATUS_PROT;
chan_byte[chan] = BUFF_CHNEND;
irq_pend = 1;
return 1;
}
k = key[addr >> 9];
if ((k & 0xf0) != sk) {
//fprintf(stderr, "Prot %08x %x %x\n\r", addr << 2, k, sk);
chan_status[chan] |= STATUS_PROT;
chan_byte[chan] = BUFF_CHNEND;
irq_pend = 1;
return 1;
}
}
M[addr] = chan_buf[chan];
sim_debug(DEBUG_CDATA, &cpu_dev, "Channel readf %02x %06x %08x %08x '",
chan, ccw_addr[chan] & 0xFFFFFC, chan_buf[chan], ccw_count[chan]);
for(k = 24; k >= 0; k -= 8) {
unsigned char ch = ebcdic_to_ascii[(chan_buf[chan] >> k) & 0xFF];
if (ch < 0x20 || ch == 0xff)
ch = '.';
sim_debug(DEBUG_CDATA, &cpu_dev, "%c", ch);
}
sim_debug(DEBUG_CDATA, &cpu_dev, "'\n");
return 0;
}
/*
* Load in the next CCW, return 1 if failure, 0 if success.
*/
int
load_ccw(uint16 chan, int tic_ok) {
uint32 word;
int cmd = 0;
UNIT *uptr;
loop:
/* Abort if channel not on double boundry */
if ((caw[chan] & 0x7) != 0) {
chan_status[chan] |= STATUS_PCHK;
return 1;
}
/* Abort if we have any errors */
if (chan_status[chan] & 0x7f)
return 1;
/* Check if we have status modifier set */
if (chan_status[chan] & STATUS_MOD) {
caw[chan]+=8;
caw[chan] &= PMASK|AMASK; /* Mask overflow bits */
chan_status[chan] &= ~STATUS_MOD;
}
/* Read in next CCW */
readfull(chan, caw[chan], &word);
sim_debug(DEBUG_CMD, &cpu_dev, "Channel read ccw %02x %06x %08x\n",
chan, caw[chan], word);
/* TIC can't follow TIC nor be first in chain */
if (((word >> 24) & 0xf) == CMD_TIC) {
if (tic_ok) {
caw[chan] = (caw[chan] & PMASK) | (word & AMASK);
tic_ok = 0;
goto loop;
}
chan_status[chan] |= STATUS_PCHK;
irq_pend = 1;
return 1;
}
caw[chan] += 4;
caw[chan] &= PMASK|AMASK; /* Mask overflow bits */
/* Check if not chaining data */
if ((ccw_flags[chan] & FLAG_CD) == 0) {
ccw_cmd[chan] = (word >> 24) & 0xff;
cmd = 1;
}
/* Set up for this command */
ccw_addr[chan] = word & AMASK;
ccw_addr[chan] |= caw[chan] & PMASK; /* Copy key */
readfull(chan, caw[chan], &word);
sim_debug(DEBUG_CMD, &cpu_dev, "Channel read ccw2 %02x %06x %08x\n",
chan, caw[chan], word);
caw[chan]+=4;
caw[chan] &= PMASK|AMASK; /* Mask overflow bits */
ccw_count[chan] = word & 0xffff;
/* Copy SLI indicator on CD command */
if ((ccw_flags[chan] & (FLAG_CD|FLAG_SLI)) == (FLAG_CD|FLAG_SLI))
word |= (FLAG_SLI<<16);
ccw_flags[chan] = (word >> 16) & 0xffff;
chan_byte[chan] = BUFF_EMPTY;
/* Check invalid count */
if (ccw_count[chan] == 0) {
chan_status[chan] |= STATUS_PCHK;
irq_pend = 1;
return 1;
}
if (cmd) {
DIB *dibp = dev_unit[chan_dev[chan]];
/* Check if invalid command */
if ((ccw_cmd[chan] & 0xF) == 0) {
chan_status[chan] |= STATUS_PCHK;
irq_pend = 1;
return 1;
}
uptr = find_chan_dev(chan_dev[chan]);
if (uptr == 0)
return 1;
chan_status[chan] &= 0xff;
chan_status[chan] |= dibp->start_cmd(uptr, chan, ccw_cmd[chan]) << 8;
if (chan_status[chan] & (STATUS_ATTN|STATUS_CHECK|STATUS_EXPT)) {
chan_status[chan] |= STATUS_CEND;
ccw_flags[chan] = 0;
ccw_cmd[chan] = 0;
irq_pend = 1;
return 1;
}
if (chan_status[chan] & (STATUS_DEND|STATUS_CEND)) {
chan_status[chan] |= STATUS_CEND;
chan_byte[chan] = BUFF_NEWCMD;
ccw_cmd[chan] = 0;
irq_pend = 1;
}
}
if (ccw_flags[chan] & FLAG_PCI) {
chan_status[chan] |= STATUS_PCI;
ccw_flags[chan] &= ~FLAG_PCI;
irq_pend = 1;
sim_debug(DEBUG_CMD, &cpu_dev, "Set PCI %02x\n", chan);
}
return 0;
}
/* read byte from memory */
int
chan_read_byte(uint16 addr, uint8 *data) {
int chan = find_subchan(addr);
int byte;
int k;
/* Abort if we have any errors */
if (chan < 0)
return 1;
if (chan_status[chan] & 0x7f)
return 1;
if ((ccw_cmd[chan] & 0x1) == 0) {
return 1;
}
/* Check if finished transfer */
if (chan_byte[chan] == BUFF_CHNEND)
return 1;
/* Check if count is zero */
if (ccw_count[chan] == 0) {
/* If not data channing, let device know there will be no
* more data to come
*/
if ((ccw_flags[chan] & FLAG_CD) == 0) {
chan_status[chan] |= STATUS_CEND;
chan_byte[chan] = BUFF_CHNEND;
sim_debug(DEBUG_DETAIL, &cpu_dev, "chan_read_end\n");
return 1;
} else {
/* If chaining try and start next CCW */
if (load_ccw(chan, 1))
return 1;
}
}
/* Read in next work if buffer is in empty status */
if (chan_byte[chan] == BUFF_EMPTY) {
if (readbuff(chan))
return 1;
chan_byte[chan] = ccw_addr[chan] & 0x3;
ccw_addr[chan] += 4 - chan_byte[chan];
}
/* Return current byte */
ccw_count[chan]--;
byte = (chan_buf[chan] >> (8 * (3 - (chan_byte[chan] & 0x3)))) & 0xff;
chan_byte[chan]++;
*data = byte;
return 0;
}
/* write byte to memory */
int
chan_write_byte(uint16 addr, uint8 *data) {
int chan = find_subchan(addr);
int byte;
int offset;
int k;
uint32 mask;
/* Abort if we have any errors */
if (chan < 0)
return 1;
if (chan_status[chan] & 0x7f)
return 1;
if ((ccw_cmd[chan] & 0x1) != 0) {
return 1;
}
/* Check if at end of transfer */
if (chan_byte[chan] == BUFF_CHNEND) {
if ((ccw_flags[chan] & FLAG_SLI) == 0) {
chan_status[chan] |= STATUS_LENGTH;
}
return 1;
}
/* Check if count is zero */
if (ccw_count[chan] == 0) {
/* Flush the buffer if we got anything back. */
if (chan_byte[chan] & BUFF_DIRTY) {
if (writebuff(chan))
return 1;
}
/* If not data channing, let device know there will be no
* more data to come
*/
if ((ccw_flags[chan] & FLAG_CD) == 0) {
chan_byte[chan] = BUFF_CHNEND;
if ((ccw_flags[chan] & FLAG_SLI) == 0) {
sim_debug(DEBUG_DETAIL, &cpu_dev, "chan_write_ length\n");
chan_status[chan] |= STATUS_LENGTH;
}
sim_debug(DEBUG_DETAIL, &cpu_dev, "chan_write_end\n");
return 1;
}
/* Otherwise try and grab next CCW */
if (load_ccw(chan, 1))
return 1;
}
/* If we are skipping, just adjust count */
if (ccw_flags[chan] & FLAG_SKIP) {
ccw_count[chan]--;
chan_byte[chan] = BUFF_EMPTY;
if ((ccw_cmd[chan] & 0xf) == CMD_RDBWD)
ccw_addr[chan]--;
else
ccw_addr[chan]++;
return 0;
}
/* Check if we need to save what we have */
if (chan_byte[chan] == (BUFF_EMPTY|BUFF_DIRTY)) {
if (writebuff(chan))
return 1;
if ((ccw_cmd[chan] & 0xf) == CMD_RDBWD)
ccw_addr[chan] -= 1 + (ccw_addr[chan] & 0x3);
else
ccw_addr[chan] += 4 - (ccw_addr[chan] & 0x3);
chan_byte[chan] = BUFF_EMPTY;
}
if (chan_byte[chan] == BUFF_EMPTY) {
if (readbuff(chan))
return 1;
chan_byte[chan] = ccw_addr[chan] & 0x3;
}
/* Store it in buffer and adjust pointer */
ccw_count[chan]--;
offset = 8 * (chan_byte[chan] & 0x3);
mask = 0xff000000 >> offset;
chan_buf[chan] &= ~mask;
chan_buf[chan] |= ((uint32)(*data)) << (24 - offset);
if ((ccw_cmd[chan] & 0xf) == CMD_RDBWD) {
if (chan_byte[chan] & 0x3)
chan_byte[chan]--;
else
chan_byte[chan] = BUFF_EMPTY;
} else
chan_byte[chan]++;
chan_byte[chan] |= BUFF_DIRTY;
return 0;
}
/*
* A device wishes to inform the CPU it needs some service.
*/
void
set_devattn(uint16 addr, uint8 flags) {
int chan = find_subchan(addr);
if (chan < 0)
return;
if (chan_dev[chan] == addr && (chan_status[chan] & STATUS_CEND) != 0 &&
(flags & SNS_DEVEND) != 0) {
chan_status[chan] |= ((uint16)flags) << 8;
} else
dev_status[addr] = flags;
sim_debug(DEBUG_EXP, &cpu_dev, "set_devattn(%x, %x) %x\n",
addr, flags, chan_dev[chan]);
irq_pend = 1;
}
/*
* Signal end of transfer by device.
*/
void
chan_end(uint16 addr, uint8 flags) {
int chan = find_subchan(addr);
if (chan < 0)
return;
sim_debug(DEBUG_DETAIL, &cpu_dev, "chan_end(%x, %x) %x\n", addr, flags, ccw_count[chan]);
/* If PCI flag set, trigger interrupt */
if (ccw_flags[chan] & FLAG_PCI) {
chan_status[chan] |= STATUS_PCI;
ccw_flags[chan] &= ~FLAG_PCI;
irq_pend = 1;
}
/* Flush buffer if there was any change */
if (chan_byte[chan] & BUFF_DIRTY) {
if (writebuff(chan))
return;
chan_byte[chan] = BUFF_EMPTY;
}
chan_status[chan] |= STATUS_CEND;
chan_status[chan] |= ((uint16)flags) << 8;
ccw_cmd[chan] = 0;
/* If count not zero and not suppressing length, report error */
if (ccw_count[chan] != 0 && (ccw_flags[chan] & FLAG_SLI) == 0) {
sim_debug(DEBUG_DETAIL, &cpu_dev, "chan_end length\n");
chan_status[chan] |= STATUS_LENGTH;
ccw_flags[chan] = 0;
}
if (flags & (SNS_ATTN|SNS_UNITCHK|SNS_UNITEXP))
ccw_flags[chan] = 0;
/* If channel is also finished, then skip any more data commands. */
if (chan_status[chan] & (STATUS_DEND|STATUS_CEND)) {
chan_byte[chan] = BUFF_NEWCMD;
/* While command has chain data set, continue to skip */
while ((ccw_flags[chan] & FLAG_CD)) {
if (load_ccw(chan, 1))
break;
if ((ccw_flags[chan] & FLAG_SLI) == 0) {
sim_debug(DEBUG_DETAIL, &cpu_dev, "chan_end length\n");
chan_status[chan] |= STATUS_LENGTH;
ccw_flags[chan] = 0;
}
}
}
irq_pend = 1;
}
/*
* Save full csw.
*/
int
store_csw(uint16 chan) {
M[0x40 >> 2] = caw[chan];
M[0x44 >> 2] = (((uint32)ccw_count[chan])) | ((uint32)chan_status[chan]<<16);
if (chan_status[chan] & STATUS_PCI) {
chan_status[chan] &= ~STATUS_PCI;
ccw_flags[chan] &= ~FLAG_PCI;
} else {
chan_status[chan] = 0;
ccw_flags[chan] &= ~FLAG_PCI;
}
sim_debug(DEBUG_EXP, &cpu_dev, "Channel store csw %02x %06x %08x\n",
chan, M[0x40>>2], M[0x44 >> 2]);
return chan_dev[chan];
}
/*
* Handle SIO instruction.
*/
int
startio(uint16 addr) {
int chan = find_subchan(addr);
DIB *dibp = dev_unit[addr];
UNIT *uptr;
uint8 status;
/* Find channel this device is on, if no none return cc=3 */
if (chan < 0 || dibp == 0)
return 3;
uptr = find_chan_dev(addr);
if (uptr == 0)
return 3;
/* If channel is active return cc=2 */
if (ccw_cmd[chan] != 0 || (ccw_flags[chan] & (FLAG_CD|FLAG_CC)) != 0 || chan_status[chan] != 0)
return 2;
sim_debug(DEBUG_CMD, &cpu_dev, "SIO %x %x %x %x\n", addr, chan,
ccw_cmd[chan], ccw_flags[chan]);
/* All ok, get caw address */
chan_status[chan] = 0;
dev_status[addr] = 0;
caw[chan] = M[0x48>>2];
chan_dev[chan] = addr;
/* If device has start_io function run it */
if (dibp->start_io != NULL) {
chan_status[chan] = dibp->start_io(uptr, chan) << 8;
if (chan_status[chan] != 0) {
M[0x44 >> 2] = ((uint32)chan_status[chan]<<16) | (M[0x44 >> 2] & 0xffff);
sim_debug(DEBUG_EXP, &cpu_dev, "Channel store csw %02x %08x\n",
chan, M[0x44 >> 2]);
chan_status[chan] = 0;
return 1;
}
}
/* Try to load first command */
if (load_ccw(chan, 0)) {
if (chan_status[chan] &
(STATUS_ATTN|STATUS_CHECK|STATUS_PROT|STATUS_PCHK|STATUS_EXPT)) {
M[0x44 >> 2] = ((uint32)chan_status[chan]<<16) | (M[0x44 >> 2] & 0xffff);
sim_debug(DEBUG_CMD, &cpu_dev, "SIO %x %x %x %x cc=2\n", addr, chan,
ccw_cmd[chan], ccw_flags[chan]);
chan_status[chan] = 0;
dev_status[addr] = 0;
ccw_cmd[chan] = 0;
return 1;
}
if (chan_status[chan] & (STATUS_PCI)) {
M[0x44 >> 2] = ((uint32)chan_status[chan]<<16) | (M[0x44 >> 2] & 0xffff);
sim_debug(DEBUG_EXP, &cpu_dev, "Channel store csw %02x %08x\n",
chan, M[0x44 >> 2]);
chan_status[chan] &= ~STATUS_PCI;
dev_status[addr] = 0;
return 1;
}
}
/* If channel returned busy save CSW and return cc=1 */
if (chan_status[chan] & STATUS_BUSY) {
M[0x40 >> 2] = 0;
M[0x44 >> 2] = ((uint32)chan_status[chan]<<16);
sim_debug(DEBUG_EXP, &cpu_dev, "Channel store csw %02x %08x\n",
chan, M[0x44 >> 2]);
chan_status[chan] = 0;
dev_status[addr] = 0;
chan_dev[chan] = 0;
ccw_cmd[chan] = 0;
return 1;
}
return 0;
}
/*
* Handle TIO instruction.
*/
int testio(uint16 addr) {
int chan = find_subchan(addr);
DIB *dibp = dev_unit[addr];
UNIT *uptr;
uint16 status;
/* Find channel this device is on, if no none return cc=3 */
if (chan < 0 || dibp == 0)
return 3;
uptr = find_chan_dev(addr);
if (uptr == 0)
return 3;
/* If any error pending save csw and return cc=1 */
if (chan_status[chan] & (STATUS_PCI|STATUS_ATTN|STATUS_CHECK|\
STATUS_PROT|STATUS_PCHK|STATUS_EXPT)) {
sim_debug(DEBUG_CMD, &cpu_dev, "TIO %x %x %x %x cc=1\n", addr, chan,
ccw_cmd[chan], ccw_flags[chan]);
store_csw(chan);
return 1;
}
/* If channel active, return cc=2 */
if (ccw_cmd[chan] != 0 || (ccw_flags[chan] & (FLAG_CD|FLAG_CC)) != 0) {
sim_debug(DEBUG_CMD, &cpu_dev, "TIO %x %x %x %x cc=2\n", addr, chan,
ccw_cmd[chan], ccw_flags[chan]);
return 2;
}
/* If error pending for another device, return cc=2 */
if (chan_dev[chan] != 0 && chan_dev[chan] != addr) {
sim_debug(DEBUG_CMD, &cpu_dev, "TIO %x %x %x %x cc=2a\n", addr, chan,
ccw_cmd[chan], ccw_flags[chan]);
return 2;
}
/* Device finished and channel status pending return it and cc=1 */
if (ccw_cmd[chan] == 0 && chan_status[chan] != 0) {
sim_debug(DEBUG_CMD, &cpu_dev, "TIO %x %x %x %x cc=1a\n", addr, chan,
ccw_cmd[chan], ccw_flags[chan]);
store_csw(chan);
chan_dev[chan] = 0;
return 1;
}
/* Device has returned a status, store the csw and return cc=1 */
if (dev_status[addr] != 0) {
sim_debug(DEBUG_CMD, &cpu_dev, "TIO %x %x %x %x cc=1b\n", addr, chan,
ccw_cmd[chan], ccw_flags[chan]);
M[0x40 >> 2] = 0;
M[0x44 >> 2] = ((uint32)dev_status[addr]) << 24;
dev_status[addr] = 0;
return 1;
}
/* Nothing pending, send a 0 command to device to get status */
status = dibp->start_cmd(uptr, chan, 0) << 8;
/* If no status and unattached device return cc=3 */
if (status == 0 && (uptr->flags & UNIT_ATT) == 0) {
sim_debug(DEBUG_CMD, &cpu_dev, "TIO %x %x %x %x cc=1c\n", addr, chan,
ccw_cmd[chan], ccw_flags[chan]);
return 3;
}
/* If we get a error, save csw and return cc=1 */
if (status & (STATUS_ATTN|STATUS_CHECK|STATUS_EXPT)) {
M[0x44 >> 2] = ((uint32)status<<24) | (M[0x44 >> 2] & 0xffff);
sim_debug(DEBUG_CMD, &cpu_dev, "TIO %x %x %x %x cc=1d\n", addr, chan,
ccw_cmd[chan], ccw_flags[chan]);
return 1;
}
/* Everything ok, return cc=0 */
sim_debug(DEBUG_CMD, &cpu_dev, "TIO %x %x %x %x cc=0\n", addr, chan,
ccw_cmd[chan], ccw_flags[chan]);
return 0;
}
/*
* Handle HIO instruction.
*/
int haltio(uint16 addr) {
int chan = find_subchan(addr);
DIB *dibp = dev_unit[addr];
UNIT *uptr;
uint8 status;
/* Find channel this device is on, if no none return cc=3 */
if (chan < 0 || dibp == 0)
return 3;
uptr = find_chan_dev(chan_dev[chan]);
if (uptr == 0)
return 3;
sim_debug(DEBUG_CMD, &cpu_dev, "HIO %x %x %x %x\n", addr, chan,
ccw_cmd[chan], ccw_flags[chan]);
/* If channel busy, set end of buffer, return cc=2 */
if (ccw_cmd[chan]) {
chan_byte[chan] = BUFF_CHNEND;
return 2;
}
/* Not executing a command, issue halt if available */
if (dibp->halt_io != NULL)
chan_status[chan] = dibp->halt_io(uptr) << 8;
return 0;
}
/*
* Handle TCH instruction.
*/
int testchan(uint16 channel) {
uint16 st = 0;
channel >>= 8;
if (channel == 0)
return 0;
if (channel > channels)
return 3;
st = chan_status[subchannels + channel];
if (st & STATUS_BUSY)
return 2;
if (st & (STATUS_ATTN|STATUS_PCI|STATUS_EXPT|STATUS_CHECK|
STATUS_PROT|STATUS_CDATA|STATUS_CCNTL|STATUS_INTER|
STATUS_CHAIN))
return 1;
return 0;
}
/*
* Bootstrap a device. Set command to READ IPL, length 24 bytes, suppress
* length warning, and chain command.
*/
t_stat chan_boot(uint16 addr, DEVICE *dptyr) {
int chan = find_subchan(addr);
DIB *dibp = dev_unit[addr];
UNIT *uptr;
uint8 status;
int i;
if (chan < 0 || dibp == 0)
return SCPE_IOERR;
for (i = 0; i < MAX_DEV; i++) {
dev_status[i] = 0;
}
for (i = 0; i < 256; i++) {
ccw_cmd[i] = 0;
ccw_flags[i] = 0;
}
uptr = find_chan_dev(addr);
chan_status[chan] = 0;
dev_status[addr] = 0;
caw[chan] = 0x8;
chan_dev[chan] = addr;
ccw_count[chan] = 24;
ccw_flags[chan] = FLAG_CC|FLAG_SLI;
ccw_addr[chan] = 0;
chan_byte[chan] = BUFF_EMPTY;
ccw_cmd[chan] = 0x2;
chan_status[chan] &= 0xff;
chan_status[chan] |= dibp->start_cmd(uptr, chan, ccw_cmd[chan]) << 8;
if (chan_status[chan] & (STATUS_ATTN|STATUS_CHECK|STATUS_EXPT)) {
ccw_flags[chan] = 0;
return SCPE_IOERR;
}
loading = addr;
return SCPE_OK;
}
/*
* Scan all channels and see if one is ready to start or has
* interrupt pending.
*/
uint16
scan_chan(uint16 mask) {
int i;
int ch;
int pend = 0; /* No device */
int imask = 0x8000;
/* Quick exit if no pending IRQ's */
if (irq_pend == 0)
return 0;
irq_pend = 0;
/* Start with channel 0 and work through all channels */
for (i = 0; i < subchannels + channels; i++) {
/* If onto channel 1 or above shift mask */
if (i >= subchannels)
imask = imask / 2;
/* Check if PCI pending */
if (chan_status[i] & STATUS_PCI) {
sim_debug(DEBUG_EXP, &cpu_dev, "Scan PCI(%x %x %x %x) end\n", i,
chan_status[i], imask, mask);
if ((imask & mask) != 0) {
pend = chan_dev[i];
break;
}
}
/* If channel end, check if we should continue */
if (chan_status[i] & STATUS_CEND) {
if (ccw_flags[i] & FLAG_CC) {
if (chan_status[i] & STATUS_DEND)
(void)load_ccw(i, 1);
else
irq_pend = 1;
} else {
sim_debug(DEBUG_EXP, &cpu_dev, "Scan(%x %x %x %x) end\n", i,
chan_status[i], imask, mask);
if ((imask & mask) != 0 || loading != 0) {
pend = chan_dev[i];
break;
}
}
}
}
/* Only return loading unit on loading */
if (loading != 0 && loading != pend)
return 0;
/* See if we can post an IRQ */
if (pend) {
irq_pend = 1;
ch = find_subchan(pend);
if (ch < 0)
return 0;
if (loading && loading == pend) {
chan_status[ch] = 0;
return pend;
}
if (ch >= 0 && loading == 0) {
sim_debug(DEBUG_EXP, &cpu_dev, "Scan end (%x %x)\n", chan_dev[ch], pend);
store_csw(ch);
return pend;
}
} else {
for (pend = 0; pend < MAX_DEV; pend++) {
if (dev_status[pend] != 0) {
ch = find_subchan(pend);
if (ch >= 0 && ccw_cmd[ch] == 0 &&
(mask & (0x8000 >> (pend >> 8))) != 0) {
irq_pend = 1;
M[0x44 >> 2] = (((uint32)dev_status[pend]) << 24);
M[0x40>>2] = 0;
sim_debug(DEBUG_EXP, &cpu_dev,
"Set atten %03x %02x [%08x] %08x\n",
ch, dev_status[pend], M[0x40 >> 2], M[0x44 >> 2]);
dev_status[pend] = 0;
return pend;
}
}
}
}
return 0;
}
t_stat
chan_set_devs()
{
int i, j;
for(i = 0; i < MAX_DEV; i++) {
dev_unit[i] = NULL; /* Device pointer */
}
/* Build channel array */
for (i = 0; sim_devices[i] != NULL; i++) {
DEVICE *dptr = sim_devices[i];
UNIT *uptr = dptr->units;
DIB *dibp = (DIB *) dptr->ctxt;
int addr;
int chan;
/* If no DIB, not channel device */
if (dibp == NULL)
continue;
/* Skip disabled devices */
if (dptr->flags & DEV_DIS)
continue;
/* Check if address is in unit or dev entry */
for (j = 0; j < dptr->numunits; j++) {
addr = GET_UADDR(uptr->u3);
if ((uptr->flags & UNIT_DIS) == 0)
dev_unit[addr] = dibp;
if (dibp->dev_ini != NULL)
dibp->dev_ini(uptr, 1);
uptr++;
}
}
return SCPE_OK;
}
/* Sets the device onto a given channel */
t_stat
set_dev_addr(UNIT * uptr, int32 val, CONST char *cptr, void *desc)
{
DEVICE *dptr;
DIB *dibp;
t_value newdev;
t_stat r;
int num;
int type;
int i;
int devaddr;
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;
newdev = get_uint (cptr, 16, 0xfff, &r);
if (r != SCPE_OK)
return r;
if ((newdev >> 8) > channels)
return SCPE_ARG;
if (newdev >= MAX_DEV)
return SCPE_ARG;
devaddr = GET_UADDR(uptr->u3);
/* Clear out existing entry */
if (dptr->flags & DEV_UADDR) {
dev_unit[devaddr] = NULL;
} else {
devaddr &= dibp->mask | 0x700;
for (i = 0; i < dibp->numunits; i++)
dev_unit[devaddr + i] = NULL;
}
/* Check if device already at newdev */
if (dptr->flags & DEV_UADDR) {
if (dev_unit[newdev] != NULL)
r = SCPE_ARG;
} else {
newdev &= dibp->mask | 0x700;
for (i = 0; i < dibp->numunits; i++) {
if (dev_unit[newdev + i] != NULL)
r = SCPE_ARG;
}
}
/* If not, point to new dev, else restore old */
if (r == SCPE_OK)
devaddr = newdev;
/* Update device entry */
if (dptr->flags & DEV_UADDR) {
dev_unit[devaddr] = dibp;
uptr->u3 &= ~UNIT_ADDR(0x7ff);
uptr->u3 |= UNIT_ADDR(devaddr);
fprintf(stderr, "Set dev %x\n\r", GET_UADDR(uptr->u3));
} else {
for (i = 0; i < dibp->numunits; i++) {
dev_unit[devaddr + i] = dibp;
uptr = &((dibp->units)[i]);
uptr->u3 &= ~UNIT_ADDR(0x7ff);
uptr->u3 |= UNIT_ADDR(devaddr + i);
fprintf(stderr, "Set dev %x\n\r", GET_UADDR(uptr->u3));
}
}
return r;
}
t_stat
show_dev_addr(FILE * st, UNIT * uptr, int32 v, CONST void *desc)
{
DEVICE *dptr;
DIB *dibp;
int addr;
if (uptr == NULL)
return SCPE_IERR;
dptr = find_dev_from_unit(uptr);
if (dptr == NULL)
return SCPE_IERR;
addr = GET_UADDR(uptr->u3);
fprintf(st, "%03x", addr);
return SCPE_OK;
}