1
0
mirror of https://github.com/rcornwell/sims.git synced 2026-01-13 15:27:04 +00:00
rcornwell.sims/SEL32/sel32_chan.c
AZBevier d198882980 SEL32: Correct real time clock interrupt processing.
SEL32: start code cleanup.
SEL32: add mfp and mfp scsi disk intitial support code.
SEL32: add new files sel32_mfp.c and sel32_scsi.c to makefile.
SEL32: modify sel32_mt.c code to define tape as BTP for UTX.
2020-03-06 21:41:59 -07:00

2191 lines
116 KiB
C

/* sel32_chan.c: SEL 32 Channel functions.
Copyright (c) 2018-2020, James C. Bevier
Portions provided by Richard Cornwell and other SIMH contributers
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
JAMES C. BEVIER 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.
Change History:
07/01/2019 - make data address mask 24 bit
07/02/2019 - make chan mask 0x7f for 128 channels max
07/18/2019 - change INCH command processing for UTX
*/
/* Handle Class E and F channel I/O operations */
#include "sel32_defs.h"
/* Class E I/O device instruction format */
/* |-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------| */
/* |00 01 02 03 04 05|06 07 08 09|10 11 12|13 14 15|16 17 18 19 20 21 22 23|24 25 26 27 28 29 30 31| */
/* | Op Code | Channel |sub-addr| Aug | Command Code | */
/* |-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------| */
/* */
/* Bits 00-05 - Op code = 0xFC */
/* Bits 00-09 - I/O channel Address (0-15) */
/* Bits 10-12 - I/O sub address (0-7) */
/* Bits 13-15 - Aug code = 6 - CD */
/* Bits 16-31 - Command Code (Device Dependent) */
/* Bits 13-15 - Aug code = 5 - TD */
/* Bits 16-18 - TD Level 2000, 4000, 8000 */
/* 01 - TD 2000 Level Status Testing */
/* 02 - TD 4000 Level Status Testing */
/* 04 - TD 8000 Level Status Testing */
/* CC1 CC2 CC3 CC4 */
/* TD8000 Undefined I/O Activ I/O Error Dev Stat Present */
/* TD4000 Invd Mem Acc Mem Parity Prog Viol Data Ovr/Undr */
/* TD2000 - Status Err - Controlr Absent */
/* Class F I/O device instruction format */
/* |-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------| */
/* |00 01 02 03 04 05|06 07 08|09 10 11 12|13 14 15|16|17 18 19 20 21 22 23|24 25 26 27 28 29 30 31| */
/* | Op Code | Reg | I/O type | Aug |0 | Channel Address | Device Sub-address | */
/* |-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------| */
/* */
/* Bits 00-06 - Op code 0xFC */
/* Bits 09-12 - I/O type */
/* 00 - Unassigned */
/* 01 - Unassigned */
/* 02 - Start I/O (SIO) */
/* 03 - Test I/O (TIO) */
/* 04 - Stop I/O (STPIO */
/* 05 - Reset channel (RSCHNL) */
/* 06 - Halt I/O (HIO) */
/* 07 - Grab controller (GRIO) Not supported */
/* 08 - Reset channel (RSCTL) */
/* 09 - Enable write channel WCS (ECWCS) Not supported */
/* 0A - Unassigned */
/* 0B - Write channel WCS (WCWCS) Not supported */
/* 0C - Enable channel interrupt (ECI) */
/* 0D - Disable channel interrupt (DCI) */
/* 0E - Activate channel interrupt (ACI) */
/* 0F - Deactivate channel interrupt (DACI) */
/* Bits 13-15 - Aug Code */
/* Bit 16 - unused - must be zero */
/* Bits 16-23 - Channel address (0-127) */
/* Bits 24-31 - Device Sub address (0-255) */
uint32 channels = MAX_CHAN; /* maximum number of channels */
int subchannels = SUB_CHANS; /* maximum number of subchannel devices */
int irq_pend = 0; /* pending interrupt flag */
extern uint32 M[]; /* our memory */
extern uint32 SPAD[]; /* CPU scratchpad memory */
extern uint32 CPUSTATUS; /* CPU status word */
extern uint32 INTS[]; /* Interrupt status flags */
/* device status */
DIB *dev_unit[MAX_DEV]; /* Pointer to Device info block */
uint16 dev_status[MAX_DEV]; /* last device status flags */
uint16 loading; /* set when booting */
#define get_chan(chsa) ((chsa>>8)&0x7f) /* get channel number from ch/sa */
/* forward definitions */
CHANP *find_chanp_ptr(uint16 chsa); /* find chanp pointer */
UNIT *find_unit_ptr(uint16 chsa); /* find unit pointer */
int chan_read_byte(uint16 chsa, uint8 *data);
int chan_write_byte(uint16 chsa, uint8 *data);
void set_devattn(uint16 chsa, uint16 flags);
void set_devwake(uint16 chsa, uint16 flags); /* wakeup O/S for async line */
void chan_end(uint16 chsa, uint16 flags);
int test_write_byte_end(uint16 chsa);
t_stat checkxio(uint16 chsa, uint32 *status); /* check XIO */
t_stat startxio(uint16 chsa, uint32 *status); /* start XIO */
t_stat testxio(uint16 chsa, uint32 *status); /* test XIO */
t_stat stoptxio(uint16 chsa, uint32 *status); /* stop XIO */
t_stat rschnlxio(uint16 chsa, uint32 *status); /* reset channel XIO */
t_stat haltxio(uint16 chsa, uint32 *status); /* halt XIO */
t_stat grabxio(uint16 chsa, uint32 *status); /* grab XIO n/u */
t_stat rsctlxio(uint16 chsa, uint32 *status); /* reset controller XIO */
uint32 find_int_icb(uint16 chsa);
uint32 find_int_lev(uint16 chsa);
uint32 scan_chan(int *ilev);
t_stat set_inch(UNIT *uptr, uint32 inch_addr); /* set channel inch address */
t_stat chan_boot(uint16 chsa, DEVICE *dptr);
t_stat chan_set_devs();
t_stat set_dev_addr(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat show_dev_addr(FILE *st, UNIT *uptr, int32 v, CONST void *desc);
DEVICE *get_dev(UNIT *uptr);
void store_csw(CHANP *chp);
/* FIFO support */
/* These are FIFO queues which return an error when full.
*
* FIFO is empty when in == out.
* If in != out, then
* - items are placed into in before incrementing in
* - items are removed from out before incrementing out
* FIFO is full when in == (out-1 + FIFO_SIZE) % FIFO_SIZE;
*
* The queue will hold FIFO_SIZE items before the calls
* to FIFO_Put fails.
*/
/* initialize FIFO to empty in boot channel code */
/* add an entry to the FIFO */
int32 FIFO_Put(uint16 chsa, uint32 entry)
{
DIB *dibp = dev_unit[chsa & 0x7f00]; /* get DIB pointer for channel */
if (dibp->chan_fifo_in == ((dibp->chan_fifo_out-1+FIFO_SIZE) % FIFO_SIZE)) {
return -1; /* FIFO Full */
}
dibp->chan_fifo[dibp->chan_fifo_in] = entry; /* add new entry */
dibp->chan_fifo_in += 1; /* next entry */
dibp->chan_fifo_in %= FIFO_SIZE; /* modulo FIFO size */
return 0; /* all OK */
}
/* get the next entry from the FIFO */
int FIFO_Get(uint16 chsa, uint32 *old)
{
DIB *dibp = dev_unit[chsa & 0x7f00]; /* get DIB pointer for channel */
/* see if the FIFO is empty */
if (dibp->chan_fifo_in == dibp->chan_fifo_out) {
return -1; /* FIFO is empty, tell caller */
}
*old = dibp->chan_fifo[dibp->chan_fifo_out]; /* get the next entry */
dibp->chan_fifo_out += 1; /* next entry */
dibp->chan_fifo_out %= FIFO_SIZE; /* modulo FIFO size */
return 0; /* all OK */
}
/* Set INCH buffer address for channel */
/* return SCPE_OK or SCPE_MEM if invalid address or SCPE_ARG if already defined */
t_stat set_inch(UNIT *uptr, uint32 inch_addr) {
uint16 chsa = GET_UADDR(uptr->u3); /* get channel & sub address */
uint32 chan = chsa & 0x7f00; /* get just channel address */
CHANP *pchp = find_chanp_ptr(chan); /* get channel prog ptr */
DIB *dibp; /* get DIB pointer for channel */
CHANP *chp;
int i;
sim_debug(DEBUG_XIO, &cpu_dev,
"set_inch chan %04x inch addr %06x\n", chan, inch_addr);
/* must be valid channel pointer */
if (pchp == NULL)
return SCPE_MEM; /* return memory error */
/* see if valid memory address */
if (inch_addr >= (MEMSIZE*4)) /* see if mem addr >= MEMSIZE */
return SCPE_MEM; /* return memory error */
#ifdef NOT_YET
/* see if inch_addr is already defined */
if (pchp->chan_inch_addr != 0)
return SCPE_ARG; /* inch already defined */
#endif
/* set parent */
pchp->chan_inch_addr = inch_addr; /* set the inch addr */
/* now go through all the sub addresses for the channel and set inch addr */
for (i=0; i<256; i++) {
chsa = chan | i; /* merge sa to real channel */
dibp = dev_unit[chsa]; /* get the device information pointer */
if (dibp == 0)
continue; /* must have a DIB, so not used */
chp = find_chanp_ptr(chsa); /* find the chanp pointer */
if (chp == 0)
continue; /* not used */
sim_debug(DEBUG_DETAIL, &cpu_dev,
"set_inch2 chsa %04x inch addr %06x\n", chsa, inch_addr);
/* set the inch buffer addr */
chp->chan_inch_addr = inch_addr; /* set inch buffer address */
}
return SCPE_OK; /* All OK */
}
/* Find interrupt level for the given device (ch/sa) */
/* return 0 if not found, otherwise level number */
uint32 find_int_lev(uint16 chsa)
{
uint32 chan, level, val;
int i;
chan = (chsa >> 8) & 0x7f; /* get channel number */
/* scan the channel entries for our chan */
for (i=0; i<128; i++) {
val = SPAD[i]; /* get spad entry */
if ((val == 0) || (val == 0xffffffff))
continue; /* not valid entry */
/* look for class F devices */
if ((val & 0x0f000000) == 0x0f000000) {
/* F class only uses chan entry */
if (((val >> 8 ) & 0x7f) == chan) {
/* channel matches, now get interrupt level number */
level = ((val >> 16) & 0x7f); /* 1's comp of int level */
level = 127 - level; /* get positive number level */
sim_debug(DEBUG_EXP, &cpu_dev,
"find_int_lev class F SPAD %08x chan %04x chsa %04x level %04x\n",
val, chan, chsa, level);
return(level); /* return the level*/
}
}
/* look for E class or class 3 device */
if (((val & 0x0f000000) == 0x0e000000) || /* E class */
((val & 0x0f800000) == 0x03800000)) { /* class 3 (interval timer) */
/* E class uses chan and device address */
if ((val & 0x7f00) == (chsa & 0x7f00)) { /* check chan/subaddress */
/* channel/subaddress matches, now get interrupt level number */
level = ((val >> 16) & 0x7f); /* 1's comp of int level */
level = 127 - level; /* get positive number level */
sim_debug(DEBUG_EXP, &cpu_dev,
"find_int_lev E SPAD %08x chan %04x chsa %04x level %04x\n", val, chan, chsa, level);
return(level); /* return the level*/
}
}
}
/* not a real device, so check interrupt entries for match */
/* scan the entries for our channel/subaddress */
for (i=0; i<112; i++) {
val = SPAD[i+0x80]; /* get spade entry */
if (val == 0 || val == 0xffffffff)
continue; /* not valid entry */
/* look for class 3 device or non device entries */
if (((val & 0x0f800000) == 0x00800000) || /* clock or external interrupt */
((val & 0x0f800000) == 0x03800000)) { /* class 3 (interval timer) */
/* E class or non I/O uses chan and device address */
if ((val & 0x7f00) == (chsa & 0x7f00)) { /* check chan/sub address */
/* channel/subaddress matches, now get interrupt level number */
level = ((val >> 16) & 0x7f); /* 1's comp of int level */
level = 127 - level; /* get positive number level */
return(level); /* return the level*/
}
}
}
return 0; /* not found */
}
/* Find interrupt context block address for given device (ch/sa) */
/* return 0 if not found, otherwise ICB memory address */
uint32 find_int_icb(uint16 chsa)
{
uint32 level, icba;
level = find_int_lev(chsa); /* find the int level */
if (level == 0)
return 0; /* not found */
icba = SPAD[0xf1] + (level<<2); /* interrupt vector address in memory */
icba = M[icba>>2]; /* get address of ICB from memory */
return(icba); /* return the address */
}
/* Find unit pointer for given device (ch/sa) */
UNIT *find_unit_ptr(uint16 chsa)
{
struct dib *dibp; /* DIB pointer */
UNIT *uptr; /* UNIT pointer */
int i;
dibp = dev_unit[chsa]; /* get DIB pointer from device pointers */
if (dibp == 0) { /* if zero, not defined on system */
return NULL; /* tell caller */
}
uptr = dibp->units; /* get the pointer to the units on this channel */
for (i = 0; i < dibp->numunits; i++) { /* search through units to get a match */
if (chsa == GET_UADDR(uptr->u3)) { /* does ch/sa match? */
return uptr; /* return the pointer */
}
uptr++; /* next unit */
}
return NULL; /* device not found on system */
}
/* Find chanp pointer for given device (ch/sa) */
CHANP *find_chanp_ptr(uint16 chsa)
{
struct dib *dibp; /* DIB pointer */
UNIT *uptr; /* UNIT pointer */
CHANP *chp; /* CHANP pointer */
int i;
dibp = dev_unit[chsa&0x7fff]; /* get DIB pointer from device pointers */
if (dibp == 0) /* if zero, not defined on system */
return NULL; /* tell caller */
if ((chp = (CHANP *)dibp->chan_prg) == NULL) { /* must have channel information for each device */
return NULL; /* tell caller */
}
uptr = dibp->units; /* get the pointer to the units on this channel */
for (i = 0; i < dibp->numunits; i++) { /* search through units to get a match */
if (chsa == GET_UADDR(uptr->u3)) { /* does ch/sa match? */
return chp; /* return the pointer */
}
uptr++; /* next UNIT */
chp++; /* next CHANP */
}
return NULL; /* device not found on system */
}
/* Read a full word into memory.
* Return 1 if fail.
* Return 0 if success.
*/
int readfull(CHANP *chp, uint32 maddr, uint32 *word)
{
maddr &= MASK24; /* mask addr to 24 bits */
if (maddr >= (MEMSIZE*4)) { /* see if mem addr >= MEMSIZE */
chp->chan_status |= STATUS_PCHK; /* program check error */
sim_debug(DEBUG_EXP, &cpu_dev, "readfull read %08x from addr %08x ERROR\n", *word, maddr<<2);
return 1; /* show we have error */
}
#define TEST
#ifdef TEST
maddr >>= 2; /* get 32 bit word index */
*word = M[maddr]; /* get the contents */
#else
*word = RMW(maddr&MASK24); /* get 1 word */
#endif
sim_debug(DEBUG_EXP, &cpu_dev, "READFULL read %08x from addr %08x\n", *word, maddr<<2);
return 0; /* return OK */
}
/* Read a word into the channel buffer.
* Return 1 if fail.
* Return 0 if success.
*/
int readbuff(CHANP *chp)
{
int k;
uint32 addr = chp->ccw_addr; /* channel buffer address */
//XXX uint16 chan = get_chan(chp->chan_dev); /* our channel */
if ((addr & MASK24) >= (MEMSIZE*4)) { /* see if memory address invalid */
chp->chan_status |= STATUS_PCHK; /* bad, program check */
sim_debug(DEBUG_EXP, &cpu_dev,
"readbuff PCHK addr %08x to big mem %08x status %04x\n",
addr, MEMSIZE, chp->chan_status);
chp->chan_byte = BUFF_CHNEND; /* force channel end */
irq_pend = 1; /* and we have an interrupt */
return 1; /* done, with error */
}
#ifdef TEST
addr &= MASK24; /* address only */
addr >>= 2; /* byte to word address */
chp->chan_buf = M[addr]; /* get 4 bytes */
#else
chp->chan_buf = RMB(addr&MASK24); /* get 1 byte */
#endif
#ifdef TEST
sim_debug(DEBUG_DETAIL, &cpu_dev,
"readbuff read memory chsa %04x into buffer %06x %06x %04x [",
chp->chan_dev, chp->ccw_addr & 0xFFFFFC, chp->chan_buf, chp->ccw_count);
for(k = 24; k >= 0; k -= 8) {
char ch = (chp->chan_buf >> k) & 0xFF;
if (ch < 0x20 || ch == 0xff)
ch = '.';
sim_debug(DEBUG_DETAIL, &cpu_dev, "%c", ch);
}
sim_debug(DEBUG_DETAIL, &cpu_dev, "]\n");
#endif
return 0;
}
/* Write 32 bit channel buffer to memory.
* Return 1 if fail.
* Return 0 if success.
*/
int writebuff(CHANP *chp)
{
uint32 addr = chp->ccw_addr;
if ((addr & MASK24) >= (MEMSIZE*4)) {
chp->chan_status |= STATUS_PCHK;
sim_debug(DEBUG_EXP, &cpu_dev,
"writebuff PCHK addr %08x to big mem %08x status %04x\n",
addr, MEMSIZE, chp->chan_status);
chp->chan_byte = BUFF_CHNEND;
irq_pend = 1;
return 1;
}
addr &= MASK24;
sim_debug(DEBUG_DETAIL, &cpu_dev,
"writebuff WRITE addr %06x DATA %08x status %04x\n",
addr, chp->chan_buf, chp->chan_status);
#ifdef TEST
M[addr>>2] = chp->chan_buf;
#else
WMB(addr, chp->chan_buf); /* write byte to memory */
#endif
return 0;
}
/* load in the IOCD and process the commands */
/* return = 0 OK */
/* return = 1 error, chan_status will have reason */
int load_ccw(CHANP *chp, int tic_ok)
{
uint32 word;
int docmd = 0;
UNIT *uptr = find_unit_ptr(chp->chan_dev); /* find the unit pointer */
uint16 chan = get_chan(chp->chan_dev); /* our channel */
loop:
sim_debug(DEBUG_XIO, &cpu_dev,
"load_ccw entry chan_status[%04x] %04x\n", chan, chp->chan_status);
/* Abort if we have any errors */
if (chp->chan_status & 0x3f03) { /* check channel status */
sim_debug(DEBUG_EXP, &cpu_dev,
"load_ccw ERROR chan_status[%04x] %04x\n", chan, chp->chan_status);
return 1;
}
/* Check if we have status modifier set */
if (chp->chan_status & STATUS_MOD) {
chp->chan_caw += 8; /* move to next IOCD */
chp->chan_status &= ~STATUS_MOD; /* turn off status modifier flag */
}
/* Read in first or next CCW */
if (readfull(chp, chp->chan_caw, &word) != 0) { /* read word from memory */
chp->chan_status |= STATUS_PCHK; /* memory read error, program check */
sim_debug(DEBUG_EXP, &cpu_dev,
"load_ccw ERROR chan_status[%04x] %04x\n", chan, chp->chan_status);
return 1; /* error return */
}
sim_debug(DEBUG_XIO, &cpu_dev,
"load_ccw read ccw chan %02x caw %06x IOCD wd 1 %08x\n",
chan, chp->chan_caw, word);
/* TIC can't follow TIC or be first in command chain */
if (((word >> 24) & 0xf) == CMD_TIC) {
if (tic_ok) {
chp->chan_caw = word & MASK24; /* get new IOCD address */
tic_ok = 0; /* another tic not allowed */
goto loop; /* restart the IOCD processing */
}
chp->chan_status |= STATUS_PCHK; /* program check for invalid tic */
sim_debug(DEBUG_EXP, &cpu_dev,
"load_ccw ERROR chan_status[%04x] %04x\n", chan, chp->chan_status);
irq_pend = 1; /* status pending */
return 1; /* error return */
}
chp->chan_caw += 4; /* point to 2nd word of the IOCD */
/* Check if not chaining data */
if ((chp->ccw_flags & FLAG_DC) == 0) {
chp->ccw_cmd = (word >> 24) & 0xff; /* not DC, so set command from IOCD wd 1 */
sim_debug(DEBUG_XIO, &cpu_dev,
"load_ccw No DC, flags %04x cmd %02x\n",
chp->ccw_flags, chp->ccw_cmd);
docmd = 1; /* show we have a command */
}
/* Set up for this command */
/* make a 24 bit address */
chp->ccw_addr = word & MASK24; /* set the data address */
if (readfull(chp, chp->chan_caw, &word) != 0) { /* read word from memory */
chp->chan_status |= STATUS_PCHK; /* memory read error, program check */
sim_debug(DEBUG_EXP, &cpu_dev,
"load_ccw data addr ERROR chan_status[%04x] %04x\n",
chan, chp->chan_status);
return 1; /* error return */
}
sim_debug(DEBUG_EXP, &cpu_dev,
"load_ccw read ccw chan %02x caw %06x IOCD wd 2 %08x\n",
chan, chp->chan_caw, word);
chp->chan_caw += 4; /* next IOCD address */
chp->ccw_count = word & 0xffff; /* get 16 bit byte count from IOCD WD 2*/
chp->chan_byte = BUFF_EMPTY; /* no bytes transferred yet */
#ifdef INVALID_FOR_INCH
/* see if buffer count 0 or < 0x8000 */
/* diags want the count from IOCD2 in status */
if (chp->ccw_count == 0) { /* see if count is zero */
chp->chan_status |= STATUS_PCHK; /* program check error */
sim_debug(DEBUG_EXP, &cpu_dev,
"load_ccw excess data count %08x ERROR chan_status[%04x] %04x\n",
chp->ccw_count, chan, chp->chan_status);
irq_pend = 1; /* status pending */
return 1; /* show we have error */
}
#endif
/* only test if non tic command */
if ((chp->ccw_cmd & 0xFF) != CMD_TIC) {
/* see if buffer address is in real memory */
/* diags want the count from IOCD2 in status */
if (chp->ccw_addr >= (MEMSIZE*4)) { /* see if mem addr >= MEMSIZE */
chp->chan_status |= STATUS_PCHK; /* program check error */
sim_debug(DEBUG_EXP, &cpu_dev,
"load_ccw data start addr %08x ERROR chan_status[%04x] %04x\n",
chp->ccw_addr, chan, chp->chan_status);
irq_pend = 1; /* status pending */
return 1; /* show we have error */
}
/* see if buffer end address is in real memory */
/* diags want the count from IOCD2 in status */
if ((chp->ccw_addr + chp->ccw_count) > (MEMSIZE*4)) { /* see if mem addr > MEMSIZE */
chp->chan_status |= STATUS_PCHK; /* program check error */
sim_debug(DEBUG_EXP, &cpu_dev,
"load_ccw data end addr %08x ERROR chan_status[%04x] %04x\n",
chp->ccw_addr, chan, chp->chan_status);
irq_pend = 1; /* status pending */
return 1; /* show we have error */
}
}
chp->ccw_flags = (word >> 16) & 0xffff; /* get flags from bits 0-7 of WD 2 of IOCD */
if (chp->ccw_flags & FLAG_PCI) { /* do we have prog controlled int? */
chp->chan_status |= STATUS_PCI; /* set PCI flag in status */
irq_pend = 1; /* interrupt pending */
}
sim_debug(DEBUG_XIO, &cpu_dev,
"load_ccw read docmd %02x irq_flag %04x count %04x chan %04x ccw_flags %04x\n",
docmd, irq_pend, chp->ccw_count, chan, chp->ccw_flags);
/* LPR sends CC cmd only without data addr/count */
#ifdef HACK_FOR_LPR
/* Check invalid count */
if ((chp->ccw_count == 0) && (chp->ccw_addr != 0)) { /* see if byte count is zero */
chp->chan_status |= STATUS_PCHK; /* program check if it is */
irq_pend = 1; /* status pending int */
return 1; /* error return */
}
#endif
if (docmd) { /* see if we need to process command */
#ifdef TRY_THIS_TUESDAY_0301
uint8 devstat;
#endif
// DEVICE *dptr = get_dev(uptr); /* find the device from unit pointer */
DIB *dibp = dev_unit[chp->chan_dev]; /* get the device pointer */
uptr = find_unit_ptr(chp->chan_dev); /* find the unit pointer */
if (uptr == 0)
return 1; /* if none, error */
sim_debug(DEBUG_XIO, &cpu_dev,
"load_ccw before start_cmd chan %04x status %04x count %04x\n",
chan, chp->chan_status, chp->ccw_count);
/* call the device startcmd function to process the current command */
#ifndef TRY_THIS_TUESDAY_0301
chp->chan_status = dibp->start_cmd(uptr, chan, chp->ccw_cmd);
#else
/* just replace device status bits */
devstat = dibp->start_cmd(uptr, chan, chp->ccw_cmd);
chp->chan_status = (chp->chan_status & 0xff00) | (devstat & 0xff);
#endif
sim_debug(DEBUG_XIO, &cpu_dev,
"load_ccw after start_cmd chan %04x status %08x count %04x\n",
chan, chp->chan_status, chp->ccw_count);
/* see if bad status */
if (chp->chan_status & (STATUS_ATTN|STATUS_CHECK|STATUS_EXPT)) {
chp->chan_status |= STATUS_CEND; /* channel end status */
chp->ccw_flags = 0; /* no flags */
chp->ccw_cmd = 0; /* stop IOCD processing */
irq_pend = 1; /* int coming */
sim_debug(DEBUG_EXP, &cpu_dev,
"load_ccw bad status chan %04x status %04x\n",
chan, chp->chan_status);
return 1; /* error return */
}
/* see if command completed */
if (chp->chan_status & (STATUS_DEND|STATUS_CEND)) {
chp->chan_status |= STATUS_CEND; /* set channel end status */
chp->chan_byte = BUFF_NEWCMD; /* ready for new cmd */
chp->ccw_cmd = 0; /* stop IOCD processing */
irq_pend = 1; /* int coming */
sim_debug(DEBUG_XIO, &cpu_dev,
"load_ccw cmd complete chan %04x status %04x count %04x\n",
chan, chp->chan_status, chp->ccw_count);
/* we want to terminate if command is complete */
/* go store status and return CC2 */
/*NEW*/ return 1; /* return cmd complete */
}
}
sim_debug(DEBUG_XIO, &cpu_dev,
"load_ccw return, chan %04x status %04x count %04x irq %02x\n",
chan, chp->chan_status, chp->ccw_count, irq_pend);
return 0; /* good return */
}
/* read byte from memory */
/* write to device */
int chan_read_byte(uint16 chsa, uint8 *data)
{
int chan = get_chan(chsa); /* get the channel number */
CHANP *chp = find_chanp_ptr(chsa); /* get channel prog pointer */
int byte;
/* Abort if we have any errors */
if (chp->chan_status & 0x3f03) /* check channel status */
return 1; /* return error */
if (chp->chan_byte == BUFF_CHNEND)
return 1; /* return error */
if (chp->ccw_count == 0) { /* see if more data required */
if ((chp->ccw_flags & FLAG_DC) == 0) { /* see if Data Chain */
chp->chan_status |= STATUS_CEND; /* no, end of data */
chp->chan_byte = BUFF_CHNEND; /* buffer end too */
sim_debug(DEBUG_EXP, &cpu_dev,
"chan_read_byte no DC chan end, cnt %04x addr %06x chan %04x\n",
chp->ccw_count, chp->ccw_addr, chan);
return 1; /* return error */
} else {
/* we have data chaining, process iocl */
if (load_ccw(chp, 1)) /* process data chaining */
return 1; /* return error */
}
}
if (chp->chan_byte == BUFF_EMPTY) { /* is buffer empty? */
if (readbuff(chp)) /* read next 4 chars */
return 1; /* return error */
#ifdef TEST
chp->chan_byte = chp->ccw_addr & 0x3; /* get byte number from address */
chp->ccw_addr += 4 - chp->chan_byte; /* next byte address */
#else
chp->chan_byte = 0; /* show that we have data byte */
chp->ccw_addr += 1; /* next byte address */
#endif
}
chp->ccw_count--; /* one char less to process */
/* get the byte of data */
#ifdef TEST
byte = (chp->chan_buf >> (8 * (3 - (chp->chan_byte & 0x3)))) & 0xff;
chp->chan_byte++; /* next byte offset in word */
#else
/* get the byte of data */
byte = chp->chan_buf; /* read byte from memory */
chp->chan_byte = BUFF_EMPTY; /* buffer empty too */
#endif
*data = byte; /* return the data */
sim_debug(DEBUG_DATA, &cpu_dev, "chan_read_byte transferred %02x\n", byte);
return 0; /* good return */
}
/* test end of write byte I/O (device read) */
int test_write_byte_end(uint16 chsa)
{
CHANP *chp = find_chanp_ptr(chsa); /* get channel prog pointer */
/* see if at end of buffer */
if (chp->chan_byte == BUFF_CHNEND)
return 1; /* return done */
if (chp->ccw_count == 0) {
if (chp->chan_byte & BUFF_DIRTY) {
writebuff(chp); /* write it */
}
if ((chp->ccw_flags & FLAG_DC) == 0) { /* see if we have data chaining */
chp->chan_status |= STATUS_CEND; /* no, end of data */
chp->chan_byte = BUFF_CHNEND; /* thats all the data we want */
return 1; /* return done */
}
}
return 0; /* not done yet */
}
/* write byte to memory */
/* read from device */
int chan_write_byte(uint16 chsa, uint8 *data)
{
int chan = get_chan(chsa); /* get the channel number */
CHANP *chp = find_chanp_ptr(chsa); /* get channel prog pointer */
int offset;
uint32 mask;
/* Abort if we have any errors */
if (chp->chan_status & 0x3f03) /* check channel status */
return 1; /* return error */
/* see if at end of buffer */
if (chp->chan_byte == BUFF_CHNEND) {
sim_debug(DEBUG_EXP, &cpu_dev, "chan_write_byte BUFF_CHNEND\n");
/* if SLI not set, we have incorrect length */
if ((chp->ccw_flags & FLAG_SLI) == 0) {
sim_debug(DEBUG_EXP, &cpu_dev, "chan_write_byte 4 setting SLI ret\n");
chp->chan_status |= STATUS_LENGTH; /* set SLI */
}
return 1; /* return error */
}
if (chp->ccw_count == 0) {
sim_debug(DEBUG_EXP, &cpu_dev,
"chan_write_byte ZERO chan %04x ccw_count %04x addr %06x\n",
chan, chp->ccw_count, chp->ccw_addr);
if (chp->chan_byte & BUFF_DIRTY) {
sim_debug(DEBUG_EXP, &cpu_dev, "chan_write_byte 2 BUF DIRTY ret\n");
if (writebuff(chp)) /* write it */
return 1; /* return error */
#ifndef TEST
chp->chan_byte = BUFF_EMPTY; /* no data now */
#endif
}
if ((chp->ccw_flags & FLAG_DC) == 0) { /* see if we have data chaining */
sim_debug(DEBUG_EXP, &cpu_dev, "chan_write_byte no DC\n");
chp->chan_status |= STATUS_CEND; /* no, end of data */
chp->chan_byte = BUFF_CHNEND; /* thats all the data we want */
return 1; /* return error */
} else {
/* we have data chaining, process iocl */
sim_debug(DEBUG_EXP, &cpu_dev,
"chan_write_byte got DC, calling load_ccw chan %04x\n", chan);
if (load_ccw(chp, 1)) /* process data chaining */
return 1; /* return error */
}
}
sim_debug(DEBUG_DETAIL, &cpu_dev, "chan_write_byte non zero ccw_count[%04x]=%04x\n",
chan, chp->ccw_count);
if (chp->ccw_flags & FLAG_SKIP) {
chp->ccw_count--;
chp->chan_byte = BUFF_EMPTY;
if ((chp->ccw_cmd & 0xff) == CMD_RDBWD)
chp->ccw_addr--;
else
chp->ccw_addr++;
sim_debug(DEBUG_CMD, &cpu_dev, "chan_write_byte SKIP ret addr %08x cnt %04x\n",
chp->ccw_addr, chp->ccw_count);
return 0;
}
if (chp->chan_byte == (BUFF_EMPTY|BUFF_DIRTY)) {
if (writebuff(chp))
return 1;
sim_debug(DEBUG_DATA, &cpu_dev, "chan_write_byte BUF EMPTY|DIRTY ret\n");
#ifdef TEST
if ((chp->ccw_cmd & 0xff) == CMD_RDBWD)
chp->ccw_addr -= (1 + (chp->ccw_addr & 0x3));
else
chp->ccw_addr += (4 - (chp->ccw_addr & 0x3));
#else
if ((chp->ccw_cmd & 0xff) == CMD_RDBWD)
chp->ccw_addr -= 1;
else
chp->ccw_addr += 1;
#endif
chp->chan_byte = BUFF_EMPTY;
}
#ifdef TEST
if (chp->chan_byte == BUFF_EMPTY)
chp->chan_byte = chp->ccw_addr & 0x3;
chp->ccw_count--;
offset = 8 * (chp->chan_byte & 0x3); /* calc byte offset in word */
mask = 0xff000000 >> offset; /* build char mask */
chp->chan_buf &= ~mask; /* zero out the old byte */
chp->chan_buf |= ((uint32)(*data)) << (24 - offset); /* or in the new one */
if ((chp->ccw_cmd & 0xff) == CMD_RDBWD) {
if (chp->chan_byte & 0x3)
chp->chan_byte--;
else
chp->chan_byte = BUFF_EMPTY;
} else
chp->chan_byte++; /* next byte */
chp->chan_byte |= BUFF_DIRTY; /* we are used */
#else
chp->chan_buf = *data; /* get data byte */
chp->ccw_count--;
chp->chan_byte |= BUFF_DIRTY; /* we are used */
#endif
return 0;
}
/* post wakeup interrupt for specified async line */
void set_devwake(uint16 chsa, uint16 flags)
{
uint32 stwd1, stwd2; /* words 1&2 of stored status */
/* put sub address in byte 0 */
stwd1 = (chsa & 0xff) << 24; /* subaddress and IOCD address to SW 1 */
/* save 16 bit channel status and residual byte count in SW 2 */
stwd2 = (uint32)flags << 16;
if ((FIFO_Put(chsa, stwd1) == -1) || (FIFO_Put(chsa, stwd2) == -1)) {
fprintf(stderr, "FIFO Overflow ERROR on chsa %04x\r\n", chsa);
}
irq_pend = 1; /* wakeup controller */
}
/* post interrupt for specified channel */
void set_devattn(uint16 chsa, uint16 flags)
{
CHANP *chp = find_chanp_ptr(chsa); /* get channel prog pointer */
sim_debug(DEBUG_EXP, &cpu_dev, "set_devattn chsa %04x, flags %04x\n", chsa, flags);
if (chp->chan_dev == chsa && (chp->chan_status & STATUS_CEND) != 0 && (flags & SNS_DEVEND) != 0) {
chp->chan_status |= ((uint16)flags);
} else
dev_status[chsa] = flags; /* save device status flags */
sim_debug(DEBUG_CMD, &cpu_dev, "set_devattn(%04x, %04x) %04x\n", chsa, flags, chp->chan_dev);
irq_pend = 1;
}
/* channel operation completed */
void chan_end(uint16 chsa, uint16 flags) {
uint32 chan_icb = find_int_icb(chsa); /* get icb address */
CHANP *chp = find_chanp_ptr(chsa); /* get channel prog pointer */
sim_debug(DEBUG_EXP, &cpu_dev,
"chan_end entry chsa %04x flags %04x chan_icb %06x status %04x cmd %02x\n",
chsa, flags, chan_icb, chp->chan_status, chp->ccw_cmd);
if (chp->chan_byte & BUFF_DIRTY) {
if (writebuff(chp)) /* write remaining data */
return; /* error */
chp->chan_byte = BUFF_EMPTY; /* we are empty now */
}
chp->chan_status |= STATUS_CEND; /* set channel end */
chp->chan_status |= ((uint16)flags); /* add in the callers flags */
// chp->ccw_cmd = 0; /* reset the completed channel command */
sim_debug(DEBUG_DETAIL, &cpu_dev,
"chan_end SLI test1 chsa %04x ccw_flags %04x count %04x status %04x\n",
chsa, chp->ccw_flags, chp->ccw_count, chp->chan_status);
#ifdef HACK_HACK
/* hack - rewind had byte count of 1, so triggered this error when it is not */
/* remove until I figure out what is required */
/* FIXME */
/* test for incorrect transfer length */
if (chp->ccw_count != 0 && ((chp->ccw_flags & FLAG_SLI) == 0)) {
chp->chan_status |= STATUS_LENGTH; /* show incorrect length status */
chp->ccw_flags = 0; /* no flags */
}
#else
/* see if this is a read/write cmd */
if (((chp->ccw_cmd & 0x7) == 0x02) || ((chp->ccw_cmd & 0x7) == 0x01)) {
/* test for incorrect transfer length */
if (chp->ccw_count != 0 && ((chp->ccw_flags & FLAG_SLI) == 0)) {
chp->chan_status |= STATUS_LENGTH; /* show incorrect length status */
sim_debug(DEBUG_XIO, &cpu_dev,
"chan_end setting SLI chsa %04x count %04x ccw_flags %04x status %04x\n",
chsa, chp->ccw_count, chp->ccw_flags, chp->chan_status);
chp->ccw_flags = 0; /* no flags */
}
}
#endif
sim_debug(DEBUG_DETAIL, &cpu_dev,
"chan_end SLI2 test chsa %04x ccw_flags %04x status %04x\n",
chsa, chp->ccw_flags, chp->chan_status);
chp->ccw_cmd = 0; /* reset the completed channel command */
/* Diags do not want SLI if we have no device end status */
if ((chp->chan_status & FLAG_SLI) && ((chp->chan_status & STATUS_DEND) == 0))
chp->chan_status &= ~FLAG_SLI;
/* no flags for attention status */
if (flags & (SNS_ATTN|SNS_UNITCHK|SNS_UNITEXP)) {
chp->ccw_flags = 0; /* no flags */
}
sim_debug(DEBUG_DETAIL, &cpu_dev,
"chan_end test end chsa %04x ccw_flags %04x status %04x\n",
chsa, chp->ccw_flags, chp->chan_status);
/* test for device or controller end */
if (chp->chan_status & (STATUS_DEND|STATUS_CEND)) {
chp->chan_byte = BUFF_NEWCMD; /* clear byte flag */
while ((chp->ccw_flags & FLAG_DC)) { /* handle data chaining */
if (load_ccw(chp, 1)) /* continue channel program */
break; /* error */
if ((chp->ccw_flags & FLAG_SLI) == 0) { /* suppress incorrect length? */
chp->chan_status |= STATUS_LENGTH; /* no, show incorrect length */
chp->ccw_flags = 0; /* no flags */
}
}
sim_debug(DEBUG_XIO, &cpu_dev,
"chan_end IOCL done chsa %04x ccw_flags %04x status %04x\n",
chsa, chp->ccw_flags, chp->chan_status);
#ifndef NEW_METHOD
/* If channel end, check if we should continue */
if (chp->chan_status & STATUS_CEND) { /* do we have channel end */
sim_debug(DEBUG_XIO, &cpu_dev,
"chan_end chan end chsa %04x flags %04x status %04x\n",
chsa, chp->ccw_flags, chp->chan_status);
if (chp->ccw_flags & FLAG_CC) { /* command chain flag */
/* we have channel end and CC flag, continue channel prog */
sim_debug(DEBUG_XIO, &cpu_dev,
"chan_end chan end & CC chsa %04x status %04x\n",
chsa, chp->chan_status);
if (chp->chan_status & STATUS_DEND) { /* device end? */
sim_debug(DEBUG_XIO, &cpu_dev,
"chan_end dev end & CC chsa %04x status %04x\n",
chsa, chp->chan_status);
(void)load_ccw(chp, 1); /* go load the next IOCB */
} else
irq_pend = 1; /* still pending int */
} else {
/* we have channel end and no CC flag, end command */
chsa = chp->chan_dev; /* get the chan/sa */
sim_debug(DEBUG_XIO, &cpu_dev,
"chan_end chan end & no CC chsa %04x status %04x\n",
chsa, chp->chan_status);
dev_status[chsa] = 0; /* no device status anymore */
/* we have completed channel program */
/* handle case where we are loading the O/S on boot */
/* if loading, leave status to be discovered by scan_chan */
if (!loading) {
sim_debug(DEBUG_DETAIL, &cpu_dev,
"chan_end call store_csw dev end & chan end chsa %04x cpustatus %08x\n",
chsa, CPUSTATUS);
store_csw(chp); /* store the status */
}
else
sim_debug(DEBUG_DETAIL, &cpu_dev,
"chan_end we are loading with DE & CE, keep status chsa %04x status %08x\n",
chsa, chp->chan_status);
//XXX irq_pend = 1; /* still pending int */
}
}
else
sim_debug(DEBUG_DETAIL, &cpu_dev,
"chan_end IOCL2 done chsa %04x ccw_flags %04x status %04x\n",
chsa, chp->ccw_flags, chp->chan_status);
#endif
}
//XXX irq_pend = 1; /* flag to test for int condition */
}
/* store the device status into the status DW in memory */
/* the INCH command provides the status address in memory */
/* return the icb address */
void store_csw(CHANP *chp)
{
uint32 stwd1, stwd2; /* words 1&2 of stored status */
uint32 chsa = chp->chan_dev; /* get ch/sa information */
/* put sub address in byte 0 */
stwd1 = ((chsa & 0xff) << 24) | chp->chan_caw; /* subaddress and IOCD address to SW 1 */
/* save 16 bit channel status and residual byte count in SW 2 */
stwd2 = ((uint32)chp->chan_status << 16) | ((uint32)chp->ccw_count);
if ((FIFO_Put(chsa, stwd1) == -1) || (FIFO_Put(chsa, stwd2) == -1)) {
fprintf(stderr, "FIFO Overflow ERROR on chsa %04x\r\n", chsa);
}
sim_debug(DEBUG_IRQ, &cpu_dev,
"store_csw FIFO write chsa %04x sw1 %08x sw2 %08x CPU stat %08x\n",
chsa, stwd1, stwd2, SPAD[0xf9]);
chp->chan_status = 0; /* no status anymore */
irq_pend = 1; /* wakeup controller */
}
/* SIO CC status returned to caller */
/* val condition */
/* 0 command accepted, will echo status - no CC's */
/* 1 channel busy - CC4 */
/* 2 channel inop or undefined (operator intervention required) - CC3 */
/* 3 sub channel busy CC3 + CC4 */
/* 4 status stored - CC2 */
/* 5 unsupported transaction CC2 + CC4 */
/* 6 unassigned CC2 + CC3 */
/* 7 unassigned CC2 + CC3 + CC4 */
/* 8 command accepted/queued, no echo status - CC1 */
/* 9 unassigned */
/* a unassigned */
/* b unassigned */
/* c unassigned */
/* d unassigned */
/* e unassigned */
/* f unassigned */
/* check an XIO operation */
/* chan channel number 0-7f */
/* suba unit address within channel 0-ff */
/* Condition codes to return 0-f as specified above */
t_stat checkxio(uint16 lchsa, uint32 *status) {
int lchan = get_chan(lchsa); /* get the logical channel number */
DIB *dibp; /* device information pointer */
UNIT *uptr; /* pointer to unit in channel */
uint32 chan_ivl; /* Interrupt Level ICB address for channel */
uint32 iocla; /* I/O channel IOCL address int ICB */
uint32 tempa, inta, spadent, chan;
uint16 chsa;
CHANP *chp;
/* get the device entry for the logical channel in SPAD */
spadent = SPAD[lchan]; /* get spad device entry for logical channel */
chan = (spadent & 0x7f00) >> 8; /* get real channel */
chsa = (chan << 8) | (lchsa & 0xff); /* merge sa to real channel */
dibp = dev_unit[chsa & 0x7f00]; /* get the device information pointer */
chp = find_chanp_ptr(chsa); /* find the chanp pointer */
uptr = find_unit_ptr(chsa); /* find pointer to unit on channel */
sim_debug(DEBUG_DETAIL, &cpu_dev, "checkxio 1 chsa %04x chan %04x\n", chsa, chan);
if (dibp == 0 || uptr == 0) { /* if no dib or unit ptr, CC3 on return */
*status = CC3BIT; /* not found, so CC3 */
sim_debug(DEBUG_XIO, &cpu_dev,
"checkxio chsa %04x is not found, CC3 return\n", chsa);
return SCPE_OK; /* not found, CC3 */
}
if ((uptr->flags & UNIT_ATTABLE) && ((uptr->flags & UNIT_ATT) == 0)) { /* is unit attached? */
sim_debug(DEBUG_XIO, &cpu_dev,
"checkxio chsa %04x is not attached, CC3 return\n", chsa);
*status = CC3BIT; /* not attached, so error CC3 */
return SCPE_OK; /* not found, CC3 */
}
/* see if interrupt is setup in SPAD and determine IVL for channel */
sim_debug(DEBUG_DETAIL, &cpu_dev, "checkxio dev spad %08x chsa %04x\n", spadent, chsa);
inta = ((spadent & 0x007f0000) >> 16); /* 1's complement of chan int level */
inta = 127 - inta; /* get positive int level */
spadent = SPAD[inta + 0x80]; /* get interrupt spad entry */
sim_debug(DEBUG_DETAIL, &cpu_dev, "checkxio int spad %08x inta %04x chan %04x\n", spadent, inta, chan);
/* get the address of the interrupt IVL in main memory */
chan_ivl = SPAD[0xf1] + (inta<<2); /* contents of spad f1 points to chan ivl in mem */
chan_ivl = M[chan_ivl >> 2]; /* get the interrupt context block addr in memory */
iocla = M[(chan_ivl+16)>>2]; /* iocla is in wd 4 of ICB */
tempa = M[(chan_ivl+20)>>2]; /* status is in wd 5 of ICB */
sim_debug(DEBUG_CMD, &cpu_dev,
"checkxio busy ck1 chsa %04x cmd %02x iocla %06x flags %04x\n",
chsa, chp->ccw_cmd, iocla, chp->ccw_flags);
sim_debug(DEBUG_CMD, &cpu_dev,
"checkxio busy ck2 chsa %04x IOCD1 %08x IOCD2 %08x INCH %08x\n",
chsa, M[iocla>>2], M[(iocla+4)>>2], tempa);
/* check for a Command or data chain operation in progresss */
if (chp->ccw_cmd != 0 || (chp->ccw_flags & (FLAG_DC|FLAG_CC)) != 0) {
sim_debug(DEBUG_XIO, &cpu_dev,
"checkxio busy return CC4 chsa %04x chan %04x\n", chsa, chan);
*status = CC4BIT; /* busy, so CC4 */
return SCPE_OK; /* just busy CC4 */
}
/* determine if channel DIB has a pre startio command processor */
if (dibp->pre_io != NULL) { /* NULL if no startio function */
/* call the device controller to get prestart_io status */
tempa = dibp->pre_io(uptr, chan); /* get status from device */
if (tempa != 0) { /* see if sub channel status is ready */
/* The device must be busy or something, but it is not ready. Return busy */
sim_debug(DEBUG_XIO, &cpu_dev,
"checkxio CC3 & CC4 return busy chan %04x cstat %08x\n",
chan, tempa);
*status = CC3BIT|CC4BIT; /* sub channel busy, so CC3|CC4 */
return SCPE_OK; /* just busy or something, CC3|CC4 */
}
}
*status = CC1BIT; /* CCs = 1, CMD accepted & queued, no echo status */
sim_debug(DEBUG_XIO, &cpu_dev, "checkxio done CC1 status %08x\n", *status);
return SCPE_OK; /* No CC's all OK */
}
/* start an XIO operation */
/* chan channel number 0-7f */
/* suba unit address within channel 0-ff */
/* Condition codes to return 0-f as specified above */
t_stat startxio(uint16 lchsa, uint32 *status) {
int lchan = get_chan(lchsa); /* get the logical channel number */
DIB *dibp; /* device information pointer */
UNIT *uptr; /* pointer to unit in channel */
uint32 chan_ivl; /* Interrupt Level ICB address for channel */
uint32 iocla; /* I/O channel IOCL address int ICB */
uint32 tempa, inta, spadent, chan;
uint16 chsa;
CHANP *chp;
/* get the device entry for the logical channel in SPAD */
spadent = SPAD[lchan]; /* get spad device entry for logical channel */
chan = (spadent & 0x7f00) >> 8; /* get real channel */
chsa = (chan << 8) | (lchsa & 0xff); /* merge sa to real channel */
dibp = dev_unit[chsa & 0x7f00]; /* get the device information pointer */
chp = find_chanp_ptr(chsa); /* find the chanp pointer */
uptr = find_unit_ptr(chsa); /* find pointer to unit on channel */
sim_debug(DEBUG_XIO, &cpu_dev, "startxio 1 chsa %04x chan %04x\n", chsa, chan);
if (dibp == 0 || uptr == 0) { /* if no dib or unit ptr, CC3 on return */
*status = CC3BIT; /* not found, so CC3 */
return SCPE_OK; /* not found, CC3 */
}
sim_debug(DEBUG_XIO, &cpu_dev, "startxio 2 chsa %04x chan %04x\n", chsa, chan);
if ((uptr->flags & UNIT_ATTABLE) && ((uptr->flags & UNIT_ATT) == 0)) { /* is unit attached? */
fprintf(stderr, "startxio chsa %04x is not attached, error return\r\n", chsa);
*status = CC3BIT; /* not attached, so error CC3 */
return SCPE_OK; /* not found, CC3 */
}
/* see if interrupt is setup in SPAD and determine IVL for channel */
sim_debug(DEBUG_XIO, &cpu_dev, "startxio dev spad %08x chsa %04x chan %04x\n", spadent, chsa, chan);
inta = ((spadent & 0x007f0000) >> 16); /* 1's complement of chan int level */
inta = 127 - inta; /* get positive int level */
spadent = SPAD[inta + 0x80]; /* get interrupt spad entry */
sim_debug(DEBUG_XIO, &cpu_dev, "startxio int spad %08x inta %04x chan %04x\n", spadent, inta, chan);
/* get the address of the interrupt IVL in main memory */
chan_ivl = SPAD[0xf1] + (inta<<2); /* contents of spad f1 points to chan ivl in mem */
chan_ivl = M[chan_ivl >> 2]; /* get the interrupt context block addr in memory */
iocla = M[(chan_ivl+16)>>2]; /* iocla is in wd 4 of ICB */
sim_debug(DEBUG_XIO, &cpu_dev,
"startxio test chsa %04x cmd %02x iocla %08x flags %04x IOCD1 %08x IOCD2 %08x\n",
chsa, chp->ccw_cmd, iocla, chp->ccw_flags, M[iocla>>2], M[(iocla+4)>>2]);
sim_debug(DEBUG_CMD, &cpu_dev, "$$$ SIO %04x %04x cmd %02x flags %04x\n",
chsa, chan, chp->ccw_cmd, chp->ccw_flags);
/* check for a Command or data chain operation in progresss */
if (chp->ccw_cmd != 0 || (chp->ccw_flags & (FLAG_DC|FLAG_CC)) != 0) {
sim_debug(DEBUG_XIO, &cpu_dev,
"startxio busy return CC3&CC4 chsa %04x chan %04x\n", chsa, chan);
*status = CC4BIT|CC3BIT; /* busy, so CC3&CC4 */
return SCPE_OK; /* just busy CC3&CC4 */
}
/* determine if channel DIB has a pre startio command processor */
if (dibp->pre_io != NULL) { /* NULL if no startio function */
/* call the device controller to get prestart_io status */
tempa = dibp->pre_io(uptr, chan); /* get status from device */
if (tempa != 0) { /* see if sub channel status is ready */
/* The device must be busy or something, but it is not ready. Return busy */
sim_debug(DEBUG_XIO, &cpu_dev, "startxio pre_io call return busy chan %04x cstat %08x\n",
chan, tempa);
//WAS chp->chan_status = 0; /* no status anymore */
*status = CC3BIT|CC4BIT; /* sub channel busy, so CC3|CC4 */
return SCPE_OK; /* just busy or something, CC3|CC4 */
}
sim_debug(DEBUG_XIO, &cpu_dev,
"startxio pre_io call return not busy ctan %04x cstat %08x\n",
chan, tempa);
}
/* channel not busy and ready to go, so start a new command */
chp->chan_status = 0; /* no channel status yet */
dev_status[chsa] = 0; /* no unit status either */
chp->chan_caw = iocla; /* get iocla address in memory */
/* set status words in memory to first IOCD information */
tempa = chp->chan_inch_addr; /* get inch status buffer address */
sim_debug(DEBUG_XIO, &cpu_dev,
"$$ SIO start IOCL processing chsa %04x iocla %08x incha %08x\n",
chsa, iocla, tempa);
/* start processing the IOCL */
if (load_ccw(chp, 0) || (chp->chan_status & STATUS_PCI)) {
/* we have an error or user requested interrupt, return status */
sim_debug(DEBUG_EXP, &cpu_dev, "startxio store csw CC2 chan %04x status %08x\n",
chan, chp->chan_status);
/* DIAG's want CC1 with memory access error */
if (chp->chan_status & STATUS_PCHK) {
chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* show I/O complete */
chp->chan_status &= ~STATUS_LENGTH; /* clear incorrect length */
//XXX store_csw(chp); /* store the status in the inch status dw */
dev_status[chsa] = 0; /* no device status */
//DIAG *status = CC2BIT; /* status stored, so CC2 */
*status = CC1BIT; /* CCs = 1, SIO accepted & queued, no echo status */
irq_pend = 1; /* still pending int */
return SCPE_OK; /* CC2 (0x20) status stored */
}
store_csw(chp); /* store the status in the inch status dw */
chp->chan_status &= ~STATUS_PCI; /* remove PCI status bit */
dev_status[chsa] = 0; /* no device status */
*status = CC2BIT; /* status stored, so CC2 */
irq_pend = 1; /* still pending int */
return SCPE_OK; /* CC2 (0x20) status stored */
}
*status = CC1BIT; /* CCs = 1, SIO accepted & queued, no echo status */
sim_debug(DEBUG_CMD, &cpu_dev, "$$$ SIO done chsa %04x status %08x iocla %08x\n",
chsa, chp->chan_status, iocla);
return SCPE_OK; /* No CC's all OK */
}
/* TIO - I/O status */
t_stat testxio(uint16 lchsa, uint32 *status) { /* test XIO */
// uint32 chan = get_chan(lchsa);
int lchan = get_chan(lchsa); /* get the logical channel number */
DIB *dibp;
UNIT *uptr;
uint32 chan_ivl; /* Interrupt Level ICB address for channel */
uint32 iocla; /* I/O channel IOCL address int ICB */
uint32 tempa, inta, spadent, chan;
uint16 chsa; /* chan/subaddr */
CHANP *chp, *pchp; /* Channel prog pointers */
uint32 sw1=0, sw2=0; /* status word 1 & 2 */
/* get the device entry for the logical channel in SPAD */
spadent = SPAD[lchan]; /* get spad device entry for logical channel */
chan = (spadent & 0x7f00) >> 8; /* get real channel */
chsa = (chan << 8) | (lchsa & 0xff); /* merge sa to real channel */
dibp = dev_unit[chsa]; /* get the device information pointer */
chp = find_chanp_ptr(chsa); /* find the device chanp pointer */
uptr = find_unit_ptr(chsa); /* find pointer to unit on channel */
pchp = find_chanp_ptr(chsa & 0x7f00); /* find the channel chanp pointer */
sim_debug(DEBUG_DETAIL, &cpu_dev, "testxio 1 chsa %04x chan %04x\n", chsa, chan);
if ((dibp == 0) || (uptr == 0)) { /* if non found, CC3 on return */
*status = CC3BIT; /* not found, so CC3 */
goto tioret; /* not found, CC3 */
}
sim_debug(DEBUG_DETAIL, &cpu_dev, "testxio 2 chsa %04x chan %04x\n", chsa, chan);
if ((uptr->flags & UNIT_ATTABLE) && ((uptr->flags & UNIT_ATT) == 0)) { /* is unit attached? */
*status = CC3BIT; /* not attached, so error CC3 */
goto tioret; /* not found, CC3 */
}
/* see if interrupt is setup in SPAD and determine IVL for channel */
sim_debug(DEBUG_DETAIL, &cpu_dev, "testxio dev spad %08x chsa %04x chan %04x\n", spadent, chsa, chan);
/* the startio opcode processing software has already checked for F class */
inta = ((spadent & 0x007f0000) >> 16); /* 1's complement of chan int level */
inta = 127 - inta; /* get positive int level */
spadent = SPAD[inta + 0x80]; /* get interrupt spad entry */
sim_debug(DEBUG_DETAIL, &cpu_dev, "testxio int spad %08x inta %04x chan %04x\n", spadent, inta, chan);
/* get the address of the interrupt IVL in main memory */
chan_ivl = SPAD[0xf1] + (inta<<2); /* contents of spad f1 points to chan ivl in mem */
chan_ivl = M[chan_ivl >> 2]; /* get the interrupt context block addr in memory */
iocla = M[(chan_ivl+16)>>2]; /* iocla is in wd 4 of ICB */
sim_debug(DEBUG_XIO, &cpu_dev,
"testxio busy test chsa %04x cmd %02x flags %04x IOCD1 %08x IOCD2 %08x IOCLA %08x\n",
chsa, chp->ccw_cmd, chp->ccw_flags, M[iocla>>2], M[(iocla+4)>>2], iocla);
/* check for a Command or data chain operation in progresss */
if (chp->ccw_cmd != 0 || (chp->ccw_flags & (FLAG_DC|FLAG_CC)) != 0) {
sim_debug(DEBUG_CMD, &cpu_dev,
"testxio busy return CC4 chsa %04x chan %04x\n", chsa, chan);
*status = CC4BIT; /* busy, so CC4 */
goto tioret; /* just busy CC4 */
}
//#define NEW_LOOK
#ifdef NEW_LOOK
/* see if status already posted and requesting interrupt */
if (INTS[inta] & INTS_REQ) {
/* we have status and a request, tell caller */
*status = CC2BIT; /* status stored from SIO, so CC2 */
INTS[inta] &= ~INTS_REQ; /* turn off request bit */
goto tioret; /* CC2 and OK */
}
#endif
/* the channel is not busy, see if any status to post */
/* see if the FIFO is empty */
if (dibp->chan_fifo_in != dibp->chan_fifo_out) {
if ((FIFO_Get(chsa, &sw1) == 0) && (FIFO_Get(chsa, &sw2) == 0)) {
uint32 chan_icb = find_int_icb(chsa); /* get icb address */
sim_debug(DEBUG_IRQ, &cpu_dev,
"testxio FIFO status stored OK, sw1 %08x sw2 %08x\n", sw1, sw2);
/* we have status to return, do it now */
tempa = pchp->chan_inch_addr; /* get inch status buffer address */
M[tempa >> 2] = sw1; /* save sa & IOCD address in status WD 1 loc */
/* save the status to memory */
M[(tempa+4) >> 2] = sw2; /* save status and residual cnt in status WD 2 loc */
/* now store the status dw address into word 5 of the ICB for the channel */
M[(chan_icb + 20) >> 2] = tempa | BIT1; /* post sw addr in ICB+5w & set CC2 in INCH addr */
*status = CC2BIT; /* status stored from SIO, so CC2 */
goto tioret; /* CC2 and OK */
}
}
/* nothing going on, so say all OK */
*status = CC1BIT; /* request accepted, no status, so CC1 */
tioret:
sim_debug(DEBUG_CMD, &cpu_dev,
"$$$ TIO END chsa %04x chan %04x cmd %02x flags %04x chan_stat %04x CCs %08x\n",
chsa, chan, chp->ccw_cmd, chp->ccw_flags, chp->chan_status, *status);
return SCPE_OK; /* No CC's all OK */
}
/* Stop XIO */
t_stat stopxio(uint16 lchsa, uint32 *status) { /* stop XIO */
int chan = get_chan(lchsa);
DIB *dibp;
UNIT *uptr;
uint32 chan_ivl; /* Interrupt Level ICB address for channel */
uint32 iocla; /* I/O channel IOCL address int ICB */
uint32 inta, spadent;
uint16 chsa;
CHANP *chp;
/* get the device entry for the logical channel in SPAD */
spadent = SPAD[chan]; /* get spad device entry for logical channel */
chan = (spadent & 0x7f00) >> 8; /* get real channel */
chsa = (chan << 8) | (lchsa & 0xff); /* merge sa to real channel */
dibp = dev_unit[chsa]; /* get the device information pointer */
chp = find_chanp_ptr(chsa); /* find the chanp pointer */
uptr = find_unit_ptr(chsa); /* find pointer to unit on channel */
sim_debug(DEBUG_CMD, &cpu_dev, "stopxio 1 chsa %04x chan %04x\n", chsa, chan);
if (dibp == 0 || uptr == 0) { /* if no dib or unit ptr, CC3 on return */
*status = CC3BIT; /* not found, so CC3 */
return SCPE_OK; /* not found, CC3 */
}
sim_debug(DEBUG_CMD, &cpu_dev, "stopxio 2 chsa %04x chan %04x\n", chsa, chan);
if ((uptr->flags & UNIT_ATTABLE) && ((uptr->flags & UNIT_ATT) == 0)) { /* is unit attached? */
*status = CC3BIT; /* not attached, so error CC3 */
return SCPE_OK; /* not found, CC3 */
}
/* see if interrupt is setup in SPAD and determine IVL for channel */
sim_debug(DEBUG_CMD, &cpu_dev, "stopxio dev spad %08x chsa %04x\n", spadent, chsa);
/* the startio opcode processing software has already checked for F class */
inta = ((spadent & 0x007f0000) >> 16); /* 1's complement of chan int level */
inta = 127 - inta; /* get positive int level */
spadent = SPAD[inta + 0x80]; /* get interrupt spad entry */
sim_debug(DEBUG_CMD, &cpu_dev, "stopxio int spad %08x inta %08x chsa %04x\n", spadent, inta, chsa);
/* get the address of the interrupt IVL in main memory */
chan_ivl = SPAD[0xf1] + (inta<<2); /* contents of spad f1 points to chan ivl in mem */
chan_ivl = M[chan_ivl >> 2]; /* get the interrupt context block addr in memory */
iocla = M[(chan_ivl+16)>>2]; /* iocla is in wd 4 of ICB */
sim_debug(DEBUG_CMD, &cpu_dev,
"stopxio busy test chsa %04x cmd %02x flags %04x IOCD1 %08x IOCD2 %08x\n",
chsa, chp->ccw_cmd, chp->ccw_flags, M[iocla>>2], M[(iocla+4)>>2]);
sim_debug(DEBUG_CMD, &cpu_dev, "$$$ STOPIO %04x %02x %04x\n",
chsa, chp->ccw_cmd, chp->ccw_flags);
/* check for a Command or data chain operation in progresss */
if (chp->ccw_cmd != 0 || (chp->ccw_flags & (FLAG_DC|FLAG_CC)) != 0) {
sim_debug(DEBUG_CMD, &cpu_dev, "stopxio busy return CC4 chsa %04x chan %04x\n", chsa, chan);
/* reset the DC or CC bits to force completion after current IOCD */
chp->ccw_flags &= ~(FLAG_DC|FLAG_CC); /* reset chaining bits */
dev_status[chsa] |= STATUS_ECHO; /* show we stopped the cmd */
*status = CC1BIT; /* request accepted, no status, so CC1 */
return SCPE_OK; /* just busy CC4 */
}
/* the channel is not busy, so return OK */
*status = CC1BIT; /* request accepted, no status, so CC1 */
sim_debug(DEBUG_CMD, &cpu_dev,
"$$$ STOPIO good return chsa %04x chan %04x cmd %02x flags %04x status %04x\n",
chsa, chan, chp->ccw_cmd, chp->ccw_flags, *status);
return SCPE_OK; /* No CC's all OK */
}
/* Reset Channel XIO */
t_stat rschnlxio(uint16 lchsa, uint32 *status) { /* reset channel XIO */
DIB *dibp;
UNIT *uptr;
uint32 spadent;
uint16 chsa;
CHANP *chp;
int lev, i;
uint32 chan = get_chan(lchsa);
/* get the device entry for the logical channel in SPAD */
spadent = SPAD[chan]; /* get spad device entry for logical channel */
chan = spadent & 0x7f00; /* get real channel */
chsa = chan; /* use just channel */
dibp = dev_unit[chsa]; /* get the device information pointer */
chp = find_chanp_ptr(chsa); /* find the chanp pointer */
uptr = find_unit_ptr(chsa); /* find pointer to unit on channel */
sim_debug(DEBUG_XIO, &cpu_dev, "rschnlxio 1 chan %04x SPAD %08x\n", chsa, spadent);
if (dibp == 0 || uptr == 0) { /* if no dib or unit ptr, CC3 on return */
*status = CC3BIT; /* not found, so CC3 */
return SCPE_OK; /* not found, CC3 */
}
sim_debug(DEBUG_XIO, &cpu_dev, "rschnlxio 2 chan %04x, spad %08x\r\n", chsa, spadent);
if ((uptr->flags & UNIT_ATTABLE) && ((uptr->flags & UNIT_ATT) == 0)) { /* is unit attached? */
*status = CC3BIT; /* not attached, so error CC3 */
return SCPE_OK; /* not found, CC3 */
}
/* reset the FIFO pointers */
dibp->chan_fifo_in = 0;
dibp->chan_fifo_out = 0;
dev_status[chan] = 0; /* clear the channel status location */
chp->chan_inch_addr = 0; /* remove inch status buffer address */
lev = find_int_lev(chan); /* get our int level */
INTS[lev] &= ~INTS_ACT; /* clear level active */
SPAD[lev+0x80] &= ~SINT_ACT; /* clear spad too */
INTS[lev] &= ~INTS_REQ; /* clear level request */
/* now go through all the sa for the channel and stop any IOCLs */
for (i=0; i<256; i++) {
chsa = chan | i; /* merge sa to real channel */
dibp = dev_unit[chsa]; /* get the device information pointer */
if (dibp == 0)
{
continue; /* not used */
}
chp = find_chanp_ptr(chsa); /* find the chanp pointer */
if (chp == 0) {
continue; /* not used */
}
dev_status[chsa] = 0; /* clear device status */
chp->chan_status = 0; /* clear the channel status */
chp->chan_byte = BUFF_EMPTY; /* no data yet */
chp->ccw_addr = 0; /* clear buffer address */
chp->chan_caw = 0x0; /* clear IOCD address */
chp->ccw_count = 0; /* channel byte count 0 bytes*/
chp->ccw_flags = 0; /* clear flags */
chp->ccw_cmd = 0; /* read command */
chp->chan_inch_addr = 0; /* clear inch addr */
}
sim_debug(DEBUG_XIO, &cpu_dev, "rschnlxio return CC1 chan %04x lev %04x\n", chan, lev);
*status = CC1BIT; /* request accepted, no status, so CC1 TRY THIS */
return SCPE_OK; /* All OK */
}
/* HIO - Halt I/O */
t_stat haltxio(uint16 lchsa, uint32 *status) { /* halt XIO */
int chan = get_chan(lchsa);
DIB *dibp;
UNIT *uptr;
uint32 chan_ivl; /* Interrupt Level ICB address for channel */
uint32 iocla; /* I/O channel IOCL address int ICB */
uint32 inta, spadent;
uint16 chsa;
CHANP *chp, *pchp; /* Channel prog pointers */
uint32 sw1=0, sw2=0; /* status word 1 & 2 */
/* get the device entry for the logical channel in SPAD */
spadent = SPAD[chan]; /* get spad device entry for logical channel */
chan = (spadent & 0x7f00) >> 8; /* get real channel */
chsa = (chan << 8) | (lchsa & 0xff); /* merge sa to real channel */
dibp = dev_unit[chsa]; /* get the device information pointer */
chp = find_chanp_ptr(chsa); /* find the chanp pointer */
uptr = find_unit_ptr(chsa); /* find pointer to unit on channel */
pchp = find_chanp_ptr(chsa & 0x7f00); /* find the parent channel pointer */
sim_debug(DEBUG_XIO, &cpu_dev, "haltxio 1 chsa %04x chan %04x\n", chsa, chan);
if (dibp == 0 || uptr == 0) { /* if no dib or unit ptr, CC3 on return */
*status = CC3BIT; /* not found, so CC3 */
return SCPE_OK; /* not found, CC3 */
}
sim_debug(DEBUG_XIO, &cpu_dev, "haltxio 2 chsa %04x chan %04x\n", chsa, chan);
if ((uptr->flags & UNIT_ATTABLE) && ((uptr->flags & UNIT_ATT) == 0)) { /* is unit attached? */
*status = CC3BIT; /* not attached, so error CC3 */
return SCPE_OK; /* not found, CC3 */
}
/* see if interrupt is setup in SPAD and determine IVL for channel */
sim_debug(DEBUG_XIO, &cpu_dev, "haltxio dev spad %08x chsa %04x chan %04x\n", spadent, chsa, chan);
/* the startio opcode processing software has already checked for F class */
inta = ((spadent & 0x007f0000) >> 16); /* 1's complement of chan int level */
inta = 127 - inta; /* get positive int level */
spadent = SPAD[inta + 0x80]; /* get interrupt spad entry */
sim_debug(DEBUG_XIO, &cpu_dev, "haltxio int spad %08x inta %02x chan %04x\n", spadent, inta, chan);
/* get the address of the interrupt IVL in main memory */
chan_ivl = SPAD[0xf1] + (inta<<2); /* contents of spad f1 points to chan ivl in mem */
chan_ivl = M[chan_ivl >> 2]; /* get the interrupt context block addr in memory */
iocla = M[(chan_ivl+16)>>2]; /* iocla is in wd 4 of ICB */
sim_debug(DEBUG_XIO, &cpu_dev,
"haltxio busy test chsa %04x chan %04x cmd %02x flags %04x IOCD1 %08x IOCD2 %08x\n",
chsa, chan, chp->ccw_cmd, chp->ccw_flags, M[iocla>>2], M[(iocla+4)>>2]);
sim_debug(DEBUG_XIO, &cpu_dev, "$$$ HIO %04x %04x %02x %04x\n",
chsa, chan, chp->ccw_cmd, chp->ccw_flags);
if (chp->ccw_cmd == 0 && (chp->ccw_flags & (FLAG_DC|FLAG_CC)) == 0) {
/* the channel is not busy, so return OK */
#ifdef DIAGTUE
*status = CC1BIT; /* request accepted, no status, so CC1 */
sim_debug(DEBUG_CMD, &cpu_dev,
"$$$ HIO END chsa %04x chan %04x cmd %02x flags %04x status %04x\n",
chsa, chan, chp->ccw_cmd, chp->ccw_flags, *status);
return SCPE_OK; /* No CC's all OK */
#else
/* diag wants an interrupt for a non busy HIO ??? */
sim_debug(DEBUG_XIO, &cpu_dev,
"$$$ HIO DIAG chsa %04x chan %04x cmd %02x flags %04x status %04x\n",
chsa, chan, chp->ccw_cmd, chp->ccw_flags, *status);
dev_status[chsa] |= STATUS_ECHO; /* show we stopped the cmd */
chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* show I/O complete */
//XXX store_csw(chp); /* store the status in the inch status dw */
dev_status[chsa] = 0; /* no device status */
irq_pend = 1; /* still pending int */
// *status = CC2BIT; /* sub channel status posted, CC2BIT */
*status = CC1BIT; /* request accepted, no status, so CC1 */
goto hioret; /* CC2 and OK */
#endif
}
chp->chan_byte = BUFF_CHNEND; /* thats all the data we want */
#ifndef TRY_THIS_02282020
/* see if we have a haltio device entry */
if (dibp->halt_io != NULL) { /* NULL if no haltio function */
/* call the device controller to get halt_io status */
uint32 tempa = dibp->halt_io(uptr); /* get status from device */
#ifdef BAD_TUE
if (tempa == SNS_BSY) {
*status = CC3BIT|CC4BIT; /* sub channel busy, CC3BIT & CC4BIT */
irq_pend = 1; /* still pending int */
sim_debug(DEBUG_CMD, &cpu_dev,
"haltxio halt_io return busy chan %04x CC3|CC4\n", chan);
goto hioret; /* just return */
}
#endif
/* test for SCPE_IOERR */
if (tempa != 0) { /* sub channel has status ready */
/* The device I/O has been terminated and status stored. */
sim_debug(DEBUG_XIO, &cpu_dev,
"haltxio halt_io call return ERROR chan %04x retstat %08x cstat %08x\n",
chan, tempa, chp->chan_status);
/*TUE*/ chp->chan_status &= ~STATUS_LENGTH; /* remove SLI status bit */
chp->chan_status &= ~STATUS_PCI; /* remove PCI status bit */
//TUE dev_status[chsa] |= STATUS_ECHO; /* show we stopped the cmd */
/* chan_end called in hio device service routine */
//XXX store_csw(chp); /* store the status in the inch status dw */
chp->ccw_count = 0; /* force zero count */
dev_status[chsa] = 0; /* no device status */
#ifndef TRY_02282020
//WAS0229 if (CPU_MODEL >= MODEL_V6) {
if (CPU_MODEL >= MODEL_27) {
/* UTX wants the status posted now, MPX wants interrupt */
/* the channel is not busy, see if any status to post */
if ((FIFO_Get(chsa, &sw1) == 0) && (FIFO_Get(chsa, &sw2) == 0)) {
uint32 chan_icb = find_int_icb(chsa); /* get icb address */
sim_debug(DEBUG_IRQ, &cpu_dev,
"haltxio 0 FIFO status stored OK, sw1 %08x sw2 %08x\n", sw1, sw2);
/*TUE*/ sw2 &= ~STATUS_LENGTH; /* remove SLI status bit */
sw2 |= STATUS_ECHO; /* show we stopped the cmd */
/* we have status to return, do it now */
tempa = pchp->chan_inch_addr; /* get inch status buffer address */
M[tempa >> 2] = sw1; /* save sa & IOCD address in status WD 1 loc */
/* save the status to memory */
M[(tempa+4) >> 2] = sw2; /* save status and count in status WD 2 loc */
/* now store the status dw address into word 5 of the ICB for the channel */
/* post sw addr in ICB+5w & set CC2 in INCH addr */
M[(chan_icb + 20) >> 2] = tempa | BIT2;
sim_debug(DEBUG_IRQ, &cpu_dev,
"haltxio 1 FIFO status stored OK, sw1 %08x sw2 %08x\n", sw1, sw2);
irq_pend = 1; /* still pending int */
// UTX likes this return and does not panic */
// The diag's want an interrupt generated, so wait
*status = CC2BIT; /* status stored from HIO, so CC2 */
/* if 0 returned, UTX hangs on input */
goto hioret; /* CC2 and OK */
}
/* nothing going on, so say all OK */
*status = CC1BIT; /* request accepted, no status, so CC1 */
//TRYIED *status = 0; /* CCs = 0, accepted */
goto hioret; /* CC2 and OK */
} else
#endif
{
sim_debug(DEBUG_IRQ, &cpu_dev,
"haltxio FIFO 2 status stored OK, sw1 %08x sw2 %08x\n", sw1, sw2);
irq_pend = 1; /* still pending int */
/*LAST*/ *status = CC1BIT; /* request accepted, no status, so CC1 */
//TRIED *status = CC2BIT; /* sub channel status posted, CC2BIT */
goto hioret; /* CC2 and OK */
}
} else {
/* we have completed the I/O without error */
/* the channel is not busy, so return OK */
//WAS *status = 0; /* CCs = 0, accepted */
sim_debug(DEBUG_XIO, &cpu_dev,
"$$$ HALTXIO good return chsa %04x chan %04x cmd %02x flags %04x status %04x\n",
chsa, chan, chp->ccw_cmd, chp->ccw_flags, *status);
*status = CC1BIT; /* request accepted, no status, so CC1 */
goto hioret; /* just return */
}
}
#endif /*TRY_THIS_02282020*/
/* device does not have a HIO entry, so terminate the I/O */
/* check for a Command or data chain operation in progresss */
if (chp->ccw_cmd != 0 || (chp->ccw_flags & (FLAG_DC|FLAG_CC)) != 0) {
sim_debug(DEBUG_XIO, &cpu_dev, "haltxio busy return CC4 chsa %04x chan %04x\n", chsa, chan);
/* reset the DC or CC bits to force completion after current IOCD */
chp->ccw_flags &= ~(FLAG_DC|FLAG_CC); /* reset chaining bits */
dev_status[chsa] |= STATUS_ECHO; /* show we stopped the cmd */
/*ADD*/ chp->ccw_count = 0; /* clear remaining count */
chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* show I/O complete */
/*ADD*/ chp->chan_status &= ~STATUS_LENGTH; /* remove SLI status bit */
//XXX store_csw(chp); /* store the status in the inch status dw */
chp->chan_status &= ~STATUS_PCI; /* remove PCI status bit */
dev_status[chsa] = 0; /* no device status */
irq_pend = 1; /* still pending int */
*status = CC2BIT; /* sub channel status posted, CC2BIT */
//TRY *status = CC1BIT; /* request accepted, no status, so CC1 */
goto hioret; /* just return */
}
/* the channel is not busy, so return OK */
*status = CC1BIT; /* request accepted, no status, so CC1 */
goto hioret; /* just return */
hioret:
sim_debug(DEBUG_CMD, &cpu_dev, "$$$ HIO END chsa %04x chan %04x cmd %02x flags %04x status %04x\n",
chsa, chan, chp->ccw_cmd, chp->ccw_flags, *status);
return SCPE_OK; /* No CC's all OK */
}
/* grab controller n/u */
/* TODO return unimplemented function error, not busy */
t_stat grabxio(uint16 lchsa, uint32 *status) { /* grab controller XIO n/u */
int chan = get_chan(lchsa);
uint32 spadent;
uint16 chsa;
CHANP *chp;
/* get the device entry for the logical channel in SPAD */
spadent = SPAD[chan]; /* get spad device entry for logical channel */
chan = (spadent & 0x7f00) >> 8; /* get real channel */
chsa = (chan << 8) | (lchsa & 0xff); /* merge sa to real channel */
chp = find_chanp_ptr(chsa); /* find the chanp pointer */
/* check for a Command or data chain operation in progresss */
if (chp->ccw_cmd != 0 || (chp->ccw_flags & (FLAG_DC|FLAG_CC)) != 0) {
sim_debug(DEBUG_CMD, &cpu_dev, "grabxio busy return CC4 chsa %04x chan %04x\n", chsa, chan);
*status = CC4BIT; /* busy, so CC4 */
return SCPE_OK; /* CC4 all OK */
}
*status = 0; /* not busy, no CC */
sim_debug(DEBUG_CMD, &cpu_dev, "grabxio chsa %04x chan %04x\n", chsa, chan);
return 0;
}
/* reset controller XIO */
t_stat rsctlxio(uint16 lchsa, uint32 *status) { /* reset controller XIO */
DIB *dibp;
UNIT *uptr;
uint32 spadent;
uint16 chsa;
CHANP *chp;
int lev, i;
uint32 chan = get_chan(lchsa);
/* get the device entry for the logical channel in SPAD */
spadent = SPAD[chan]; /* get spad device entry for logical channel */
chan = spadent & 0x7f00; /* get real channel */
chsa = chan; /* use just channel */
dibp = dev_unit[chsa]; /* get the device information pointer */
chp = find_chanp_ptr(chsa); /* find the chanp pointer */
uptr = find_unit_ptr(chsa); /* find pointer to unit on channel */
sim_debug(DEBUG_CMD, &cpu_dev, "rsctlxio 1 chan %04x SPAD %08x\n", chsa, spadent);
if (dibp == 0 || uptr == 0) { /* if no dib or unit ptr, CC3 on return */
*status = CC3BIT; /* not found, so CC3 */
return SCPE_OK; /* not found, CC3 */
}
sim_debug(DEBUG_CMD, &cpu_dev, "rsctlxio 2 chan %04x, spad %08x\r\n", chsa, spadent);
if ((uptr->flags & UNIT_ATTABLE) && ((uptr->flags & UNIT_ATT) == 0)) { /* is unit attached? */
*status = CC3BIT; /* not attached, so error CC3 */
return SCPE_OK; /* not found, CC3 */
}
/* reset the FIFO pointers */
dibp->chan_fifo_in = 0;
dibp->chan_fifo_out = 0;
dev_status[chan] = 0; /* clear the channel status location */
chp->chan_inch_addr = 0; /* remove inch status buffer address */
lev = find_int_lev(chan); /* get our int level */
INTS[lev] &= ~INTS_ACT; /* clear level active */
SPAD[lev+0x80] &= ~SINT_ACT; /* clear spad too */
INTS[lev] &= ~INTS_REQ; /* clear level request */
/* now go through all the sa for the channel and stop any IOCLs */
for (i=0; i<256; i++) {
chsa = chan | i; /* merge sa to real channel */
dibp = dev_unit[chsa]; /* get the device information pointer */
if (dibp == 0)
{
continue; /* not used */
}
chp = find_chanp_ptr(chsa); /* find the chanp pointer */
if (chp == 0) {
continue; /* not used */
}
dev_status[chsa] = 0; /* clear device status */
chp->chan_status = 0; /* clear the channel status */
chp->chan_byte = BUFF_EMPTY; /* no data yet */
chp->ccw_addr = 0; /* clear buffer address */
chp->chan_caw = 0x0; /* clear IOCD address */
chp->ccw_count = 0; /* channel byte count 0 bytes*/
chp->ccw_flags = 0; /* clear flags */
chp->ccw_cmd = 0; /* read command */
}
sim_debug(DEBUG_CMD, &cpu_dev, "rsctlxio return CC1 chan %04x lev %04x\n", chan, lev);
*status = CC1BIT; /* request accepted, no status, so CC1 TRY THIS */
return SCPE_OK; /* All OK */
}
/* boot from the device (ch/sa) the caller specified */
/* on CPU reset, the cpu has set the IOCD data at location 0-4 */
t_stat chan_boot(uint16 chsa, DEVICE *dptr) {
int chan = get_chan(chsa);
//Z DIB *dibp = dev_unit[chsa];
DIB *dibp = (DIB *)dptr->ctxt; /* get pointer to DIB for this device */
CHANP *chp = 0;
sim_debug(DEBUG_EXP, &cpu_dev, "Channel Boot chan/device addr %04x\n", chsa);
if (dibp == 0) /* if no channel or device, error */
return SCPE_IOERR; /* error */
if (dibp->chan_prg == NULL) /* must have channel information for each device */
return SCPE_IOERR; /* error */
chp = find_chanp_ptr(chsa); /* find the chanp pointer */
dev_status[chsa&0x7f00] = 0; /* clear the channel status location */
dev_status[chsa] = 0; /* device status too */
chp->chan_status = 0; /* clear the channel status */
chp->chan_dev = chsa; /* save our address (ch/sa) */
chp->chan_byte = BUFF_EMPTY; /* no data yet */
chp->ccw_addr = 0; /* start loading at loc 0 */
chp->chan_caw = 0x0; /* set IOCD address to memory location 0 */
chp->ccw_count = 0; /* channel byte count 0 bytes*/
chp->ccw_flags = 0; /* Command chain and supress incorrect length */
chp->ccw_cmd = 0; /* read command */
sim_debug(DEBUG_CMD, &cpu_dev, "Channel Boot calling load_ccw chan %04x status %08x\n",
chan, chp->chan_status);
/* start processing the boot IOCL at loc 0 */
if (load_ccw(chp, 0)) { /* load IOCL starting from location 0 */
sim_debug(DEBUG_CMD, &cpu_dev, "Channel Boot Error return from load_ccw chan %04x status %08x\n",
chan, chp->chan_status);
dev_status[chsa] = 0; /* no device status */
chp->ccw_flags = 0; /* clear the command flags */
return SCPE_IOERR; /* return error */
}
sim_debug(DEBUG_CMD, &cpu_dev, "Channel Boot OK return from load_ccw chsa %04x status %04x\n",
chsa, chp->chan_status);
loading = chsa; /* show we are loading from the boot device */
return SCPE_OK; /* all OK */
}
/* Scan all channels and see if one is ready to start or has
interrupt pending. Return icb address and interrupt level
*/
uint32 scan_chan(int *ilev) {
int i;
//XXint j;
uint32 chsa = 0; /* No device */
uint32 chan; /* channel num 0-7f */
uint32 tempa; /* icb address */
uint32 chan_ivl; /* int level table address */
uint32 chan_icba; /* int level context block address */
CHANP *chp; /* channel prog pointer */
DIB *dibp; /* DIB pointer */
#ifdef OLD_METHOD
//WAS if (irq_pend == 1) { /* pending int? */
if ((irq_pend == 1) && loading) { /* pending int? */
/* see if we have a channel completed */
/* loop through all the channels/units for channel with pending I/O completion */
for (i = 0; sim_devices[i] != NULL; i++) {
DEVICE *dptr = sim_devices[i]; /* get pointer to configured device */
DIB *dibp = (DIB *)dptr->ctxt; /* get pointer to DIB for this device */
UNIT *uptr = dptr->units; /* get pointer to units defined for this device */
if (dibp == NULL) /* If no DIB, not channel device */
continue;
if (dptr->flags & DEV_DIS) { /* Skip disabled devices */
continue;
}
if ((chp = (CHANP *)dibp->chan_prg) == NULL)/* must have channel information for each device */
continue;
/* Check if address is in unit or dev entry */
for (j = 0; j < dibp->numunits; j++) { /* loop through unit entries */
chsa = GET_UADDR(uptr->u3); /* ch/sa value */
/* If channel end, check if we should continue */
if (chp->chan_status & STATUS_CEND) { /* do we have channel end */
sim_debug(DEBUG_XIO, &cpu_dev,
"scan_chan loading %02x chan end chsa %04x flags %04x status %04x\n",
loading, chsa, chp->ccw_flags, chp->chan_status);
if (chp->ccw_flags & FLAG_CC) { /* command chain flag */
/* we have channel end and CC flag, continue channel prog */
sim_debug(DEBUG_XIO, &cpu_dev,
"scan_chan loading %02x chan end & CC chsa %04x status %04x\n",
loading, chsa, chp->chan_status);
if (chp->chan_status & STATUS_DEND) { /* device end? */
sim_debug(DEBUG_XIO, &cpu_dev,
"scan_chan loading %02x dev end & CC chsa %04x status %04x\n",
loading, chsa, chp->chan_status);
(void)load_ccw(chp, 1); /* go load the next IOCB */
} else
irq_pend = 1; /* still pending int */
} else {
/* we have channel end and no CC flag, end command */
chsa = chp->chan_dev; /* get the chan/sa */
sim_debug(DEBUG_XIO, &cpu_dev,
"scan_chan loading %02x chan end & no CC chsa %04x status %04x\n",
loading, chsa, chp->chan_status);
dev_status[chsa] = 0; /* no device status anymore */
/* handle case where we are loading the O/S on boot */
if (loading) {
if (chp->chan_status & 0x3f03) { /* check if any channel errors */
return 0; /* yes, just return */
}
irq_pend = 0; /* no pending int */
chp->chan_status = 0; /* no channel status */
return chsa; /* if loading, just channel number */
}
/* we are not loading, but have completed channel program */
store_csw(chp); /* store the status */
sim_debug(DEBUG_IRQ, &cpu_dev,
"scan_chan call store_csw dev end & chan end chsa %04x cpustatus %08x\n",
chsa, CPUSTATUS);
irq_pend = 1; /* still pending int */
//WAS goto trythis;
return 0; /* just return */
}
}
uptr++; /* next UNIT pointer */
chp++; /* next CHANP pointer */
}
}
}
//WAS trythis:
#else
if ((irq_pend == 1) && loading) { /* pending int? */
/* see if we have a channel completed for the boot channel */
chp = find_chanp_ptr(loading); /* find the chanp pointer for channel */
/* If channel end, check if we should continue */
if (chp->chan_status & STATUS_CEND) { /* do we have channel end */
sim_debug(DEBUG_DETAIL, &cpu_dev,
"scan_chan loading %02x chan end chsa %04x flags %04x status %02x\n",
loading, chsa, chp->ccw_flags, chp->chan_status);
if (chp->ccw_flags & FLAG_CC) { /* command chain flag */
/* we have channel end and CC flag, continue channel prog */
sim_debug(DEBUG_DETAIL, &cpu_dev,
"scan_chan loading %02x chan end & CC chsa %04x status %02x\n",
loading, chsa, chp->chan_status);
if (chp->chan_status & STATUS_DEND) { /* device end? */
sim_debug(DEBUG_DETAIL, &cpu_dev,
"scan_chan loading %02x dev end & CC chsa %04x status %02x\n",
loading, chsa, chp->chan_status);
(void)load_ccw(chp, 1); /* go load the next IOCB */
} else
irq_pend = 1; /* still pending int */
} else {
/* we have channel end and no CC flag, end command */
chsa = chp->chan_dev; /* get the chan/sa */
sim_debug(DEBUG_DETAIL, &cpu_dev,
"scan_chan loading %02x chan end & no CC chsa %04x status %02x\n",
loading, chsa, chp->chan_status);
dev_status[chsa] = 0; /* no device status anymore */
/* handle case where we are loading the O/S on boot */
if (chp->chan_status & 0x3f03) { /* check if any channel errors */
return 0; /* yes, just return */
}
irq_pend = 0; /* no pending int */
chp->chan_status = 0; /* no channel status */
return chsa; /* if loading, just channel number */
}
}
}
#endif
//#define NEW_LOOK
#ifdef NEW_LOOK
/* Look for channels that are not active and not requesting, */
/* but have status to post. Store the status and make them */
/* into an interest request. They will be found when we scan */
/* for the highest requesting interrupt. */
for (i=0; i<112; i++) {
if (SPAD[i+0x80] == 0) /* not initialize? */
continue; /* skip this one */
if (SPAD[i+0x80] == 0xffffffff) /* not initialize? */
continue; /* skip this one */
/* see if interrupt is enabled */
if (!((INTS[i] & INTS_ENAB) || (SPAD[i+0x80] & SINT_ENAB)))
continue; /* skip this one */
/* we are enabled, see if already active */
if ((INTS[i] & INTS_ACT) || (SPAD[i+0x80] & SINT_ACT))
continue; /* this level active, keep looking */
/* see if status already posted and requesting */
if (INTS[i] & INTS_REQ)
continue; /* this level requesting, keep looking */
/* see if there is pending status for this channel */
/* if there is, load it and request interrupt */
/* get the device entry for the logical channel in SPAD */
chan = (SPAD[i+0x80] & 0x7f00); /* get real channel and zero sa */
dibp = dev_unit[chan]; /* get the device information pointer */
/* no status for non-device entries */
if (dibp == 0) {
sim_debug(DEBUG_DETAIL, &cpu_dev,
"scan_chan %04x int %02x no DIB pointer for requesting interrupt\n", chan, i);
continue; /* skip unconfigured channel */
}
/* see if FIFO has status to post */
if (dibp->chan_fifo_in != dibp->chan_fifo_out) {
uint32 sw1, sw2;
/* fifo is not empty, so post status and request an interrupt */
if ((FIFO_Get(chan, &sw1) == 0) && (FIFO_Get(chan, &sw2) == 0)) {
/* we have status to return, do it now */
chp = find_chanp_ptr(chan); /* find the chanp pointer for channel */
/* get the address of the interrupt IVL table in main memory */
chan_ivl = SPAD[0xf1] + (i<<2); /* contents of spad f1 points to chan ivl in mem */
chan_icba = M[chan_ivl >> 2]; /* get the interrupt context blk addr in memory */
tempa = chp->chan_inch_addr; /* get inch status buffer address */
M[tempa >> 2] = sw1; /* save sa & IOCD address in status WD 1 loc */
/* save the status to memory */
M[(tempa+4) >> 2] = sw2; /* save status and count in status WD 2 loc */
/* now store the status dw address into word 5 of the ICB for the channel */
/* post sw addr in ICB+5w & set CC2 in SW */
M[(chan_icba + 20) >> 2] = tempa | BIT1;
INTS[i] |= INTS_REQ; /* turn on channel interrupt request */
sim_debug(DEBUG_IRQ, &cpu_dev,
"scan_chan REQ %04x FIFO read, irq %02x inch %06x chan_icba %06x sw1 %08x sw2 %08x\n",
chan, i, tempa, chan_icba, sw1, sw2);
irq_pend = 1; /* still pending int */
continue; /* keep looking */
}
}
/* no status to post, so go on */
}
#endif
/* see if we are able to look for ints */
if ((CPUSTATUS & 0x80) == 0) { /* are interrupts blocked */
/* ints not blocked, so look for highest requesting interrupt */
for (i=0; i<112; i++) {
if (SPAD[i+0x80] == 0) /* not initialize? */
continue; /* skip this one */
if (SPAD[i+0x80] == 0xffffffff) /* not initialize? */
continue; /* skip this one */
// if (INTS[i]&INTS_ACT) /* look for level active */
if (SPAD[i+0x80] & SINT_ACT) { /* look for level active */
sim_debug(DEBUG_IRQ, &cpu_dev,
"scan_chan INTS ACT irq %02x SPAD %08x INTS %08x\n",
i, SPAD[i+0x80], INTS[i]);
break; /* this level active, so stop looking */
}
#ifndef OLD3
/* look for the highest requesting interrupt */
/* that is enabled */
if (((INTS[i] & INTS_ENAB) && (INTS[i] & INTS_REQ)) ||
((SPAD[i+0x80] & SINT_ENAB) && (INTS[i] & INTS_REQ))) {
/* requesting, make active and turn off request flag */
INTS[i] &= ~INTS_REQ; /* turn off request */
INTS[i] |= INTS_ACT; /* turn on active */
SPAD[i+0x80] |= SINT_ACT; /* show active in SPAD too */
/* make sure both enabled too */
/* should already be enabled */
// INTS[i] |= INTS_ENAB; /* turn on enable */
// SPAD[i+0x80] |= SINT_ENAB; /* show enabled in SPAD too */
/* get the address of the interrupt IVL table in main memory */
chan_ivl = SPAD[0xf1] + (i<<2); /* contents of spad f1 points to chan ivl in mem */
chan_icba = M[chan_ivl >> 2]; /* get the interrupt context block addr in memory */
sim_debug(DEBUG_IRQ, &cpu_dev,
"scan_chan INTS REQ irq %02x found chan_icba %08x INTS %08x\n",
i, chan_icba, INTS[i]);
*ilev = i; /* return interrupt level */
irq_pend = 0; /* not pending anymore */
return(chan_icba); /* return ICB address */
}
#endif
#ifndef OLD1
/* see if there is pending status for this channel */
/* if there is and the level is not requesting, do it */
if ((INTS[i] & INTS_ENAB) && !(INTS[i] & INTS_REQ)) {
/* get the device entry for the logical channel in SPAD */
chan = (SPAD[i+0x80] & 0x7f00); /* get real channel and zero sa */
dibp = dev_unit[chan]; /* get the device information pointer */
if (dibp == 0)
continue; /* skip unconfigured channel */
/* see if the FIFO is empty */
if (dibp->chan_fifo_in != dibp->chan_fifo_out) {
uint32 sw1, sw2;
/* fifo is not empty, so post status and request an interrupt */
if ((FIFO_Get(chan, &sw1) == 0) && (FIFO_Get(chan, &sw2) == 0)) {
/* we have status to return, do it now */
chp = find_chanp_ptr(chan); /* find the chanp pointer for channel */
/* get the address of the interrupt IVL table in main memory */
chan_ivl = SPAD[0xf1] + (i<<2); /* contents of spad f1 points to chan ivl in mem */
chan_icba = M[chan_ivl >> 2]; /* get the interrupt context blk addr in memory */
tempa = chp->chan_inch_addr; /* get inch status buffer address */
M[tempa >> 2] = sw1; /* save sa & IOCD address in status WD 1 loc */
/* save the status to memory */
M[(tempa+4) >> 2] = sw2; /* save status and count in status WD 2 loc */
/* now store the status dw address into word 5 of the ICB for the channel */
/* post sw addr in ICB+5w & set CC2 in SW */
M[(chan_icba + 20) >> 2] = tempa | BIT1;
INTS[i] |= INTS_REQ; /* turn on channel interrupt request */
sim_debug(DEBUG_IRQ, &cpu_dev,
"scan_chan %04x FIFO read, set irq %02x inch %06x chan_icba %06x sw1 %08x sw2 %08x\n",
chan, i, tempa, chan_icba, sw1, sw2);
irq_pend = 1; /* still pending int */
break; /* quit looking */
}
}
}
#endif
#ifdef OLD2
/* look for the highest requesting interrupt */
/* that is enabled */
if (((INTS[i] & INTS_ENAB) && (INTS[i] & INTS_REQ)) ||
((SPAD[i+0x80] & SINT_ENAB) && (INTS[i] & INTS_REQ))) {
/* requesting, make active and turn off request flag */
INTS[i] &= ~INTS_REQ; /* turn off request */
INTS[i] |= INTS_ACT; /* turn on active */
SPAD[i+0x80] |= SINT_ACT; /* show active in SPAD too */
/* make sure both enabled too */
/* should already be enabled */
// INTS[i] |= INTS_ENAB; /* turn on enable */
// SPAD[i+0x80] |= SINT_ENAB; /* show enabled in SPAD too */
/* get the address of the interrupt IVL table in main memory */
chan_ivl = SPAD[0xf1] + (i<<2); /* contents of spad f1 points to chan ivl in mem */
chan_icba = M[chan_ivl >> 2]; /* get the interrupt context block addr in memory */
sim_debug(DEBUG_IRQ, &cpu_dev,
"scan_chan INTS REQ irq %04x found chan_icba %08x INTS %08x\n",
i, chan_icba, INTS[i]);
*ilev = i; /* return interrupt level */
irq_pend = 0; /* not pending anymore */
return(chan_icba); /* return ICB address */
}
#endif
}
}
return 0; /* done */
}
/* part of find_dev_from_unit(UNIT *uptr) in scp.c */
/* Find_dev pointer for a unit
Input: uptr = pointer to unit
Output: dptr = pointer to device
*/
DEVICE *get_dev(UNIT *uptr)
{
DEVICE *dptr = NULL;
uint32 i, j;
if (uptr == NULL) /* must be valid unit */
return NULL;
if (uptr->dptr) /* get device pointer from unit */
return uptr->dptr; /* return valid pointer */
/* the device pointer in the unit is not set up, do it now */
/* This should never happed as the pointer is setup in first reset call */
for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* do all devices */
for (j = 0; j < dptr->numunits; j++) { /* do all units for device */
if (uptr == (dptr->units + j)) { /* match found? */
uptr->dptr = dptr; /* set the pointer in unit */
return dptr; /* return valid pointer */
}
}
}
return NULL;
}
/* set up the devices configured into the simulator */
/* only devices with a DIB will be processed */
t_stat chan_set_devs() {
uint32 i, j;
for(i = 0; i < MAX_DEV; i++) {
dev_unit[i] = NULL; /* clear Device pointer array */
}
/* Build channel array */
for (i = 0; sim_devices[i] != NULL; i++) {
DEVICE *dptr = sim_devices[i]; /* get pointer to next configured device */
UNIT *uptr = dptr->units; /* get pointer to units defined for this device */
DIB *dibp = (DIB *)dptr->ctxt; /* get pointer to DIB for this device */
CHANP *chp; /* channel program pointer */
int chsa; /* addr of device chan & subaddress */
/* set the device back pointer in the unit structure */
for (j = 0; j < dptr->numunits; j++) { /* loop through unit entries */
uptr->dptr = dptr; /* set the device pointer in unit structure */
uptr++; /* next UNIT pointer */
}
uptr = dptr->units; /* get pointer to units again */
if (dibp == NULL) /* If no DIB, not channel device */
continue;
if (dptr->flags & DEV_DIS) /* Skip disabled devices */
continue;
if ((chp = (CHANP *)dibp->chan_prg) == NULL)/* must have channel information for each device */
continue;
/* Check if address is in unit or dev entry */
for (j = 0; j < dptr->numunits; j++) { /* loop through unit entries */
chsa = GET_UADDR(uptr->u3); /* ch/sa value */
//printf("Setup device %s%d chsa %04x type %03d dptr %x\n",
// dptr->name, j, chsa, GET_TYPE(uptr->flags), uptr->dptr);
/* zero some channel data loc's for device */
dev_status[chsa] = 0; /* zero device status flags */
dev_status[chsa&0x7f00] = 0; /* clear the channel status location */
chp->chan_status = 0; /* clear the channel status */
chp->chan_dev = chsa; /* save our address (ch/sa) */
chp->chan_byte = BUFF_EMPTY; /* no data yet */
chp->ccw_addr = 0; /* start loading at loc 0 */
chp->chan_caw = 0; /* set IOCD address to memory location 0 */
chp->ccw_count = 0; /* channel byte count 0 bytes*/
chp->ccw_flags = 0; /* Command chain and supress incorrect length */
chp->ccw_cmd = 0; /* read command */
chp->chan_inch_addr = 0; /* clear address of stat dw in memory */
if ((uptr->flags & UNIT_DIS) == 0) { /* is unit marked disabled? */
if (dev_unit[chsa] != 0) {
printf("Channel/SubAddress %04x multiple defined, aborting\n", chsa);
return SCPE_IERR; /* no, arg error */
}
dev_unit[chsa] = dibp; /* no, save the dib address */
}
if (dibp->dev_ini != NULL) /* if there is an init routine, call it now */
dibp->dev_ini(uptr, 1); /* init the channel */
uptr++; /* next UNIT pointer */
chp++; /* next CHANP pointer */
}
}
return SCPE_OK; /* all is OK */
}
/* Validate and set the device onto a given channel */
t_stat set_dev_addr(UNIT *uptr, int32 val, CONST char *cptr, void *desc) {
DEVICE *dptr; /* device pointer */
DIB *dibp; /* dib pointer */
UNIT *tuptr; /* temp unit pointer */
t_value chan; /* new channel addr */
t_stat r; /* return status */
int i; /* temp */
int chsa; /* dev addr */
// dptr = uptr->dptr /* get device pointer from unit */
if (cptr == NULL) /* is there a UNIT name specified */
return SCPE_ARG; /* no, arg error */
if (uptr == NULL) /* is there a UNIT pointer */
return SCPE_IERR; /* no, arg error */
dptr = get_dev(uptr); /* find the device from unit pointer */
if (dptr == NULL) /* device not found, so error */
return SCPE_IERR; /* error */
dibp = (DIB *)dptr->ctxt; /* get dib pointer from device struct */
if (dibp == NULL) /* we need a DIB */
return SCPE_IERR; /* no DIB, so error */
chan = get_uint(cptr, 16, 0xffff, &r); /* get new device address */
if (r != SCPE_OK) /* need good number */
return r; /* number error, return error */
chan &= 0x7f00; /* clean channel address */
dibp->chan_addr = chan; /* set new parent channel addr */
/* change all the unit addresses with the new channel, but keep sub address */
/* Clear out existing entries for all units on this device */
tuptr = dptr->units; /* get pointer to units defined for this device */
/* loop through all units for this device */
for (i = 0; i < dibp->numunits; i++) {
chsa = GET_UADDR(tuptr->u3); /* get old chsa for this unit */
dev_unit[chsa] = NULL; /* clear sa dib pointer */
dev_unit[chsa&0x7f00] = NULL; /* clear the channel dib address */
chsa = chan | (chsa & 0xff); /* merge new channel with new sa */
tuptr->u3 &= ~UNIT_ADDR_MASK; /* clear old chsa for this unit */
tuptr->u3 |= UNIT_ADDR(chsa); /* clear old chsa for this unit */
dev_unit[chan&0x7f00] = dibp; /* set the channel dib address */
dev_unit[chsa] = dibp; /* save the dib address for new chsa */
fprintf(stderr, "Set dev %04x to %04x\r\n", GET_UADDR(tuptr->u3), chsa);
tuptr++; /* next unit pointer */
}
return SCPE_OK;
}
t_stat show_dev_addr(FILE *st, UNIT *uptr, int32 v, CONST void *desc) {
DEVICE *dptr;
int chsa;
if (uptr == NULL) /* valid unit? */
return SCPE_IERR; /* no, error return */
dptr = get_dev(uptr); /* get the device pointer from unit */
if (dptr == NULL) /* valid pointer? */
return SCPE_IERR; /* return error */
chsa = GET_UADDR(uptr->u3); /* get the unit address */
fprintf(st, "CHAN/SA %04x", chsa); /* display channel/subaddress */
return SCPE_OK; /* we done */
}