376 lines
11 KiB
C
376 lines
11 KiB
C
/* vax610_io.c: MicroVAX I Qbus IO simulator
|
|
|
|
Copyright (c) 2011-2012, Matt Burke
|
|
This module incorporates code from SimH, Copyright (c) 1998-2008, Robert M Supnik
|
|
|
|
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
|
|
THE AUTHOR(S) 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.
|
|
|
|
Except as contained in this notice, the name(s) of the author(s) shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from the author(s).
|
|
|
|
qba Qbus adapter
|
|
|
|
15-Feb-2012 MB First Version
|
|
*/
|
|
|
|
#include "vax_defs.h"
|
|
|
|
int32 int_req[IPL_HLVL] = { 0 }; /* intr, IPL 14-17 */
|
|
int32 autcon_enb = 1; /* autoconfig enable */
|
|
|
|
extern int32 PSL, SISR, trpirq, mem_err, hlt_pin;
|
|
extern int32 p1;
|
|
extern jmp_buf save_env;
|
|
extern DEVICE *sim_devices[];
|
|
|
|
int32 eval_int (void);
|
|
t_stat qba_reset (DEVICE *dptr);
|
|
|
|
/* Qbus adapter data structures
|
|
|
|
qba_dev QBA device descriptor
|
|
qba_unit QBA units
|
|
qba_reg QBA register list
|
|
*/
|
|
|
|
UNIT qba_unit = { UDATA (NULL, 0, 0) };
|
|
|
|
REG qba_reg[] = {
|
|
{ HRDATA (IPL17, int_req[3], 32), REG_RO },
|
|
{ HRDATA (IPL16, int_req[2], 32), REG_RO },
|
|
{ HRDATA (IPL15, int_req[1], 32), REG_RO },
|
|
{ HRDATA (IPL14, int_req[0], 32), REG_RO },
|
|
{ FLDATA (AUTOCON, autcon_enb, 0), REG_HRO },
|
|
{ NULL }
|
|
};
|
|
|
|
MTAB qba_mod[] = {
|
|
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "IOSPACE", NULL,
|
|
NULL, &show_iospace },
|
|
{ MTAB_XTD|MTAB_VDV, 1, "AUTOCONFIG", "AUTOCONFIG",
|
|
&set_autocon, &show_autocon },
|
|
{ MTAB_XTD|MTAB_VDV, 0, NULL, "NOAUTOCONFIG",
|
|
&set_autocon, NULL },
|
|
{ 0 }
|
|
};
|
|
|
|
DEVICE qba_dev = {
|
|
"QBA", &qba_unit, qba_reg, qba_mod,
|
|
1, 16, 4, 2, 16, 16,
|
|
NULL, NULL, &qba_reset,
|
|
NULL, NULL, NULL,
|
|
NULL, DEV_QBUS
|
|
};
|
|
|
|
/* IO page dispatches */
|
|
|
|
t_stat (*iodispR[IOPAGESIZE >> 1])(int32 *dat, int32 ad, int32 md);
|
|
t_stat (*iodispW[IOPAGESIZE >> 1])(int32 dat, int32 ad, int32 md);
|
|
|
|
/* Interrupt request to interrupt action map */
|
|
|
|
int32 (*int_ack[IPL_HLVL][32])(void); /* int ack routines */
|
|
|
|
/* Interrupt request to vector map */
|
|
|
|
int32 int_vec[IPL_HLVL][32]; /* int req to vector */
|
|
|
|
/* The KA610 handles errors in I/O space as follows
|
|
|
|
- read: machine check
|
|
- write: machine check (?)
|
|
*/
|
|
|
|
int32 ReadQb (uint32 pa)
|
|
{
|
|
int32 idx, val;
|
|
|
|
idx = (pa & IOPAGEMASK) >> 1;
|
|
if (iodispR[idx]) {
|
|
iodispR[idx] (&val, pa, READ);
|
|
return val;
|
|
}
|
|
MACH_CHECK (MCHK_READ);
|
|
return 0;
|
|
}
|
|
|
|
void WriteQb (uint32 pa, int32 val, int32 mode)
|
|
{
|
|
int32 idx;
|
|
|
|
idx = (pa & IOPAGEMASK) >> 1;
|
|
if (iodispW[idx]) {
|
|
iodispW[idx] (val, pa, mode);
|
|
return;
|
|
}
|
|
MACH_CHECK (MCHK_WRITE); /* FIXME: is this correct? */
|
|
return;
|
|
}
|
|
|
|
/* ReadIO - read I/O space
|
|
|
|
Inputs:
|
|
pa = physical address
|
|
lnt = length (BWLQ)
|
|
Output:
|
|
longword of data
|
|
*/
|
|
|
|
int32 ReadIO (uint32 pa, int32 lnt)
|
|
{
|
|
int32 iod;
|
|
|
|
iod = ReadQb (pa); /* wd from Qbus */
|
|
if (lnt < L_LONG) /* bw? position */
|
|
iod = iod << ((pa & 2)? 16: 0);
|
|
else iod = (ReadQb (pa + 2) << 16) | iod; /* lw, get 2nd wd */
|
|
SET_IRQL;
|
|
return iod;
|
|
}
|
|
|
|
/* WriteIO - write I/O space
|
|
|
|
Inputs:
|
|
pa = physical address
|
|
val = data to write, right justified in 32b longword
|
|
lnt = length (BWLQ)
|
|
Outputs:
|
|
none
|
|
*/
|
|
|
|
void WriteIO (uint32 pa, int32 val, int32 lnt)
|
|
{
|
|
if (lnt == L_BYTE)
|
|
WriteQb (pa, val, WRITEB);
|
|
else if (lnt == L_WORD)
|
|
WriteQb (pa, val, WRITE);
|
|
else {
|
|
WriteQb (pa, val & 0xFFFF, WRITE);
|
|
WriteQb (pa + 2, (val >> 16) & 0xFFFF, WRITE);
|
|
}
|
|
SET_IRQL;
|
|
return;
|
|
}
|
|
|
|
/* Find highest priority outstanding interrupt */
|
|
|
|
int32 eval_int (void)
|
|
{
|
|
int32 ipl = PSL_GETIPL (PSL);
|
|
int32 i, t;
|
|
|
|
static const int32 sw_int_mask[IPL_SMAX] = {
|
|
0xFFFE, 0xFFFC, 0xFFF8, 0xFFF0, /* 0 - 3 */
|
|
0xFFE0, 0xFFC0, 0xFF80, 0xFF00, /* 4 - 7 */
|
|
0xFE00, 0xFC00, 0xF800, 0xF000, /* 8 - B */
|
|
0xE000, 0xC000, 0x8000 /* C - E */
|
|
};
|
|
|
|
if (hlt_pin) /* hlt pin int */
|
|
return IPL_HLTPIN;
|
|
if ((ipl < IPL_MEMERR) && mem_err) /* mem err int */
|
|
return IPL_MEMERR;
|
|
for (i = IPL_HMAX; i >= IPL_HMIN; i--) { /* chk hwre int */
|
|
if (i <= ipl) /* at ipl? no int */
|
|
return 0;
|
|
if (int_req[i - IPL_HMIN]) /* req != 0? int */
|
|
return i;
|
|
}
|
|
if (ipl >= IPL_SMAX) /* ipl >= sw max? */
|
|
return 0;
|
|
if ((t = SISR & sw_int_mask[ipl]) == 0) /* eligible req */
|
|
return 0;
|
|
for (i = IPL_SMAX; i > ipl; i--) { /* check swre int */
|
|
if ((t >> i) & 1) /* req != 0? int */
|
|
return i;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Return vector for highest priority hardware interrupt at IPL lvl */
|
|
|
|
int32 get_vector (int32 lvl)
|
|
{
|
|
int32 i;
|
|
int32 l = lvl - IPL_HMIN;
|
|
|
|
if (lvl == IPL_MEMERR) { /* mem error? */
|
|
mem_err = 0;
|
|
return SCB_MEMERR;
|
|
}
|
|
if (lvl > IPL_HMAX) { /* error req lvl? */
|
|
ABORT (STOP_UIPL); /* unknown intr */
|
|
}
|
|
for (i = 0; int_req[l] && (i < 32); i++) {
|
|
if ((int_req[l] >> i) & 1) {
|
|
int_req[l] = int_req[l] & ~(1u << i);
|
|
if (int_ack[l][i])
|
|
return int_ack[l][i]();
|
|
return int_vec[l][i];
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Reset I/O bus */
|
|
|
|
void ioreset_wr (int32 data)
|
|
{
|
|
reset_all (5); /* from qba on... */
|
|
return;
|
|
}
|
|
|
|
/* Reset Qbus */
|
|
|
|
t_stat qba_reset (DEVICE *dptr)
|
|
{
|
|
int32 i;
|
|
|
|
for (i = 0; i < IPL_HLVL; i++)
|
|
int_req[i] = 0;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Qbus I/O buffer routines, aligned access
|
|
|
|
Map_ReadB - fetch byte buffer from memory
|
|
Map_ReadW - fetch word buffer from memory
|
|
Map_WriteB - store byte buffer into memory
|
|
Map_WriteW - store word buffer into memory
|
|
*/
|
|
|
|
int32 Map_ReadB (uint32 ba, int32 bc, uint8 *buf)
|
|
{
|
|
int32 i;
|
|
uint32 ma = ba & 0x3FFFFF;
|
|
uint32 dat;
|
|
|
|
if ((ba | bc) & 03) { /* check alignment */
|
|
for (i = 0; i < bc; i++, buf++) { /* by bytes */
|
|
*buf = ReadB (ma);
|
|
ma = ma + 1;
|
|
}
|
|
}
|
|
else {
|
|
for (i = 0; i < bc; i = i + 4, buf++) { /* by longwords */
|
|
dat = ReadL (ma); /* get lw */
|
|
*buf++ = dat & BMASK; /* low 8b */
|
|
*buf++ = (dat >> 8) & BMASK; /* next 8b */
|
|
*buf++ = (dat >> 16) & BMASK; /* next 8b */
|
|
*buf = (dat >> 24) & BMASK;
|
|
ma = ma + 4;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int32 Map_ReadW (uint32 ba, int32 bc, uint16 *buf)
|
|
{
|
|
int32 i;
|
|
uint32 ma = ba & 0x3FFFFF;
|
|
uint32 dat;
|
|
|
|
ba = ba & ~01;
|
|
bc = bc & ~01;
|
|
if ((ba | bc) & 03) { /* check alignment */
|
|
for (i = 0; i < bc; i = i + 2, buf++) { /* by words */
|
|
*buf = ReadW (ma);
|
|
ma = ma + 2;
|
|
}
|
|
}
|
|
else {
|
|
for (i = 0; i < bc; i = i + 4, buf++) { /* by longwords */
|
|
dat = ReadL (ma); /* get lw */
|
|
*buf++ = dat & WMASK; /* low 16b */
|
|
*buf = (dat >> 16) & WMASK; /* high 16b */
|
|
ma = ma + 4;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int32 Map_WriteB (uint32 ba, int32 bc, uint8 *buf)
|
|
{
|
|
int32 i;
|
|
uint32 ma = ba & 0x3FFFFF;
|
|
uint32 dat;
|
|
|
|
if ((ba | bc) & 03) { /* check alignment */
|
|
for (i = 0; i < bc; i++, buf++) { /* by bytes */
|
|
WriteB (ma, *buf);
|
|
ma = ma + 1;
|
|
}
|
|
}
|
|
else {
|
|
for (i = 0; i < bc; i = i + 4, buf++) { /* by longwords */
|
|
dat = (uint32) *buf++; /* get low 8b */
|
|
dat = dat | (((uint32) *buf++) << 8); /* merge next 8b */
|
|
dat = dat | (((uint32) *buf++) << 16); /* merge next 8b */
|
|
dat = dat | (((uint32) *buf) << 24); /* merge hi 8b */
|
|
WriteL (ma, dat); /* store lw */
|
|
ma = ma + 4;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int32 Map_WriteW (uint32 ba, int32 bc, uint16 *buf)
|
|
{
|
|
int32 i;
|
|
uint32 ma = ba & 0x3FFFFF;
|
|
uint32 dat;
|
|
|
|
ba = ba & ~01;
|
|
bc = bc & ~01;
|
|
if ((ba | bc) & 03) { /* check alignment */
|
|
for (i = 0; i < bc; i = i + 2, buf++) { /* by words */
|
|
WriteW (ma, *buf);
|
|
ma = ma + 2;
|
|
}
|
|
}
|
|
else {
|
|
for (i = 0; i < bc; i = i + 4, buf++) { /* by longwords */
|
|
dat = (uint32) *buf++; /* get low 16b */
|
|
dat = dat | (((uint32) *buf) << 16); /* merge hi 16b */
|
|
WriteL (ma, dat); /* store lw */
|
|
ma = ma + 4;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Build dib_tab from device list */
|
|
|
|
t_stat build_dib_tab (void)
|
|
{
|
|
int32 i;
|
|
DEVICE *dptr;
|
|
DIB *dibp;
|
|
t_stat r;
|
|
|
|
init_ubus_tab (); /* init bus tables */
|
|
for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */
|
|
dibp = (DIB *) dptr->ctxt; /* get DIB */
|
|
if (dibp && !(dptr->flags & DEV_DIS)) { /* defined, enabled? */
|
|
if ((r = build_ubus_tab (dptr, dibp))) /* add to bus tab */
|
|
return r;
|
|
} /* end if enabled */
|
|
} /* end for */
|
|
return SCPE_OK;
|
|
}
|