1
0
mirror of https://github.com/PDP-10/klh10.git synced 2026-01-13 07:19:32 +00:00
PDP-10.klh10/src/dvuba.c
2016-02-17 20:44:40 +01:00

487 lines
13 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* DVUBA.C - KLH10 Emulation of KS10 Unibus Adapters
*/
/* $Id: dvuba.c,v 2.4 2002/05/21 09:44:52 klh Exp $
*/
/* Copyright © 1992, 1993, 2001 Kenneth L. Harrenstien
** All Rights Reserved
**
** This file is part of the KLH10 Distribution. Use, modification, and
** re-distribution is permitted subject to the terms in the file
** named "LICENSE", which contains the full text of the legal notices
** and should always accompany this Distribution.
**
** This software is provided "AS IS" with NO WARRANTY OF ANY KIND.
**
** This notice (including the copyright and warranty disclaimer)
** must be included in all copies or derivations of this software.
*/
/*
* $Log: dvuba.c,v $
* Revision 2.4 2002/05/21 09:44:52 klh
* Complain about bad unibus refs only if ub_debug set
*
* Revision 2.3 2001/11/10 21:28:59 klh
* Final 2.0 distribution checkin
*
*/
/*
** Provides interface between I/O instructions and UNIBUS devices,
** including the unibus adapters as devices in their own right.
**
** Note that unibus addresses and register values are different in
** the UBA world than they are for "normal" unibus devices.
** In particular, for the UBA code:
** Addresses: 18 bits (RH of IO-address) (18 for real devs)
** Registers: 32 bits on read; 18 on write (16 for real devs)
*/
#include "klh10.h"
#if !KLH10_CPU_KS && CENV_SYS_DECOSF
/* Stupid gubbish needed to prevent OSF/1 AXP compiler from
** halting merely because compiled file is empty!
*/
static int decosfcclossage;
#endif
#if KLH10_CPU_KS /* Moby conditional for entire file */
#include <stdio.h> /* For stderr if buggy */
#include <string.h>
#include "kn10def.h"
#include "kn10ops.h"
#include "kn10dev.h"
#include "kn10cpu.h"
#include "dvuba.h"
#ifdef RCSID
RCSID(dvuba_c,"$Id: dvuba.c,v 2.4 2002/05/21 09:44:52 klh Exp $")
#endif
/* Pre-declarations */
static void ubasta_write(struct ubctl *, h10_t);
static uint32 ubapag_read(struct ubctl *, dvuadr_t);
#define PILEV_UB1REQ cpu.pi.pilev_ub1req
#define PILEV_UB3REQ cpu.pi.pilev_ub3req
/* Unibus Adapter data structures.
** Leave external so CPU can access the PI request status.
** Easier debugging, too.
*/
struct ubctl dvub1, dvub3;
int ub_debug; /* Not a full debug trace, just prints warnings */
static void ub_pifun(struct device *, int);
static uint32 ub0_read(dvuadr_t); /* Special rtns for controller #0 */
static void ub0_write(dvuadr_t, h10_t);
/* DVUB_INIT - Initialize unibus code & data & devices
*/
void
dvub_init(void)
{
memset((char *)&dvub1, 0, sizeof(dvub1));
memset((char *)&dvub3, 0, sizeof(dvub3));
dvub1.ubn = 1;
dvub3.ubn = 3;
/* Remember place to deposit our PI requests with CPU. This is
** something of a hack, but will do for now.
*/
dvub1.ubpireqs = &cpu.pi.pilev_ub1req;
dvub3.ubpireqs = &cpu.pi.pilev_ub3req;
}
int
dvub_add(FILE *of,
register struct device *d,
int ubn)
{
register struct ubctl *ub;
register int i;
switch (ubn) {
case 1: ub = &dvub1; break;
case 3: ub = &dvub3; break;
default:
fprintf(of, "Cannot bind device to unibus #%d - only 1 and 3 allowed.\n",
ubn);
return FALSE;
}
if (ub->ubndevs >= KLH10_DEVUBA_MAX) {
fprintf(of, "Too many unibus #%d devices (limit is %d)\n",
ubn, KLH10_DEVUBA_MAX);
return FALSE;
}
/* Verify that device registers don't conflict with an existing device */
for (i = 0; i < ub->ubndevs; ++i) {
register struct device *d2 = ub->ubdevs[i];
/* For each existing dev, see if 1st addr comes after new dev's last,
* or last addr comes before new dev's first.
*/
if ((d->dv_aend <= d2->dv_addr) /* Comes after? */
|| (d->dv_addr >= d2->dv_aend)) { /* Comes before? */
continue; /* OK, keep going */
}
/* Overlap -- complain and fail */
fprintf(of, "Cannot bind device - Unibus register overlap:\r\n\
Device %-6s %06lo-%06lo\r\n\
Device %-6s %06lo-%06lo\r\n",
d->dv_name, (long)d->dv_addr, (long)d->dv_aend,
d2->dv_name, (long)d2->dv_addr, (long)d2->dv_aend);
return FALSE;
}
d->dv_uba = ub; /* Point to right unibus controller */
d->dv_pifun = ub_pifun; /* Use this to handle PI reqs */
i = ub->ubndevs;
ub->ubdevs[i+1] = NULL; /* Set fence in array */
ub->ubdevs[i] = d;
ub->ubdvadrs[i*2] = d->dv_addr;
ub->ubdvadrs[(i*2)+1] = d->dv_aend;
ub->ubndevs++;
return TRUE;
}
/* UB_PIFUN - Called from devices with BR # (int level) to assert.
** If nonzero, maps BR level to PI level and triggers PI.
** If zero, turns off PI request.
** Sorta painful cuz must check all other devices to be sure
** it's OK to turn off the corresponding PI level request.
*/
static void
ub_pifun(register struct device *d,
int br)
{
register struct ubctl *ub = d->dv_uba; /* Get right UBA for dev */
register int lev;
register struct device **dp;
switch (br) {
case 0: /* Turn off request for this device */
d->dv_pireq = 0; /* Turn off for specific device */
/* Now propagate turnoff up the chain */
/* Find new outstanding level, if any.
** This scan is a bit ugly.
*/
lev = 0;
for (dp = ub->ubdevs; *dp; ++dp) /* Check all known devs */
lev |= (*dp)->dv_pireq;
if (lev == *(ub->ubpireqs)) /* Compare new level with current */
return; /* No change, do nothing */
*(ub->ubpireqs) = lev; /* Changed! Set new */
pi_devupd(); /* Update main PI state */
return;
case 4:
case 5:
lev = ub->ublolev; /* BRs 4 and 5 are mapped to "low" PI */
break;
case 6:
case 7:
lev = ub->ubhilev; /* BRs 6 and 7 are mapped to "high" PI */
break;
default:
panic("ub_pireq: unknown BR level: %d", br);
}
/* If broke out of switch, triggering interrupt at level "lev" */
if (!lev) /* UBA may have no interrupt mapping */
return;
d->dv_pireq = lev; /* Set PI request for specific device */
if (*(ub->ubpireqs) & lev) /* This level PI already requested? */
return; /* Yep, needn't yell at CPU again */
*(ub->ubpireqs) |= lev; /* Nope, tell CPU about new PI! */
pi_devupd(); /* Update main PI state */
}
/* UB_DEVFIND - Look up device given its controller and unibus address.
** This is a slow but sure crock for now; it can be done much
** more cleverly with a hash table or even a sorted binary-searched
** order at runtime when devices are configured. Later...
*/
static struct device *
ub_devfind(register struct ubctl *ub,
register dvuadr_t addr)
{
register int i = 0, n = ub->ubndevs;
register dvuadr_t *ua = ub->ubdvadrs;
for (; i < n; ua += 2, ++i) {
if (ua[0] <= addr && addr < ua[1])
return ub->ubdevs[i];
}
return NULL; /* None found */
}
/* Auxiliaries */
#define ubasta_read(ub) ((uint32)((ub)->ubsta))
#define ubapag_write(ub,addr,val) ((ub)->ubpmap[(addr) - UB_UBAPAG] = (val))
static void
ub_badctl(char *fn, w10_t ioaddr, int bflag)
{
if (ub_debug)
fprintf(stderr, "%s: Bad UB ctlr in IO addr: %lo,,%lo\r\n",
fn, (long)LHGET(ioaddr), (long)RHGET(ioaddr));
pag_iofail(((paddr_t)LHGET(ioaddr))<<18 | RHGET(ioaddr), bflag);
}
static void
ub_badaddr(char *fn, dvuadr_t uaddr, int bflag)
{
if (ub_debug)
fprintf(stderr, "%s: Bad unibus IO addr: %lo\r\n", fn, (long)uaddr);
pag_iofail((paddr_t)uaddr, bflag);
}
/* UB_READ - Called by KS10 IO instructions to read the Unibus.
** UB_WRITE - Ditto to write
*/
uint32
ub_read(register w10_t ioaddr)
{
switch (LHGET(ioaddr)) { /* Check controller # */
case 0: return ub0_read((dvuadr_t)RHGET(ioaddr));
case 1: return uba_read(&dvub1, (dvuadr_t)RHGET(ioaddr));
case 3: return uba_read(&dvub3, (dvuadr_t)RHGET(ioaddr));
}
ub_badctl("ub_read", ioaddr, FALSE);
return 0; /* Never returns */
}
uint32
ub_bread(register w10_t ioaddr)
{
register dvuadr_t uaddr;
register uint32 reg;
uaddr = RHGET(ioaddr) & ~01; /* Get address w/o low bit */
switch (LHGET(ioaddr)) { /* Check controller # */
case 0: reg = ub0_read(uaddr); break;
case 1: reg = uba_read(&dvub1, uaddr); break;
case 3: reg = uba_read(&dvub3, uaddr); break;
default:
ub_badctl("ub_bread", ioaddr, TRUE);
/* Never returns */
}
if (RHGET(ioaddr) & 01)
reg = reg >> 8;
return reg & 0377;
}
void
ub_write(register w10_t ioaddr,
h10_t val)
{
switch (LHGET(ioaddr)) { /* Check controller # */
case 0: ub0_write((dvuadr_t)RHGET(ioaddr), val); break;
case 1: uba_write(&dvub1, (dvuadr_t)RHGET(ioaddr), val); break;
case 3: uba_write(&dvub3, (dvuadr_t)RHGET(ioaddr), val); break;
default:
ub_badctl("ub_write", ioaddr, FALSE);
/* Never returns */
}
}
void
ub_bwrite(register w10_t ioaddr,
register h10_t val)
{
register dvuadr_t uaddr;
register uint32 mask;
if ((uaddr = RHGET(ioaddr)) & 01) {
uaddr &= ~01;
mask = 0377 << 8;
val <<= 8;
} else {
mask = 0377;
}
val &= mask;
switch (LHGET(ioaddr)) { /* Check controller # */
case 0: ub0_write(uaddr, (ub0_read(uaddr) & ~mask) | val); break;
case 1: uba_write(&dvub1, uaddr,
(uba_read(&dvub1, uaddr) & ~mask) | val); break;
case 3: uba_write(&dvub3, uaddr,
(uba_read(&dvub3, uaddr) & ~mask) | val); break;
default:
ub_badctl("ub_bwrite", ioaddr, TRUE);
/* Never returns */
}
}
static uint32
ub0_read(dvuadr_t addr)
{
switch (addr) {
case UB_KSECCS: /* Memory Status Register */
return 0; /* Ignore writes, return 0 on reads */
}
ub_badaddr("ub0_read", addr, FALSE);
return 0; /* Never returns */
}
static void
ub0_write(dvuadr_t addr,
h10_t val)
{
switch (addr) {
case UB_KSECCS: /* Memory Status Register */
return; /* Ignore writes */
}
ub_badaddr("ub0_write", addr, FALSE);
return; /* Never returns */
}
uint32
uba_read(register struct ubctl *ub,
dvuadr_t addr)
{
register struct device *d;
if ((d = ub_devfind(ub, addr)))
return (uint32) (*(d->dv_read))(d, addr);
switch (addr) {
case UB_UBASTA: /* Unibus Status Register */
return ubasta_read(ub);
case UB_UBAMNT: /* Unibus Maintenance Register */
return 0; /* Ignore writes, return 0 on read */
}
if (UB_UBAPAG <= addr && addr < UB_UBAEND) {
return ubapag_read(ub, addr);
}
ub_badaddr("uba_read", addr, FALSE);
return 0; /* Never returns */
}
void
uba_write(register struct ubctl *ub,
register dvuadr_t addr,
h10_t val) /* UBA itself can take 18 bits */
{
register struct device *d;
if ((d = ub_devfind(ub, addr))) {
(*(d->dv_write))(d, addr, (dvureg_t)val);
return;
}
switch (addr) {
case UB_UBASTA: /* Unibus Status Register */
ubasta_write(ub, val);
return;
case UB_UBAMNT: /* Unibus Maintenance Register */
return; /* Ignore writes, return 0 on read */
}
if (UB_UBAPAG <= addr && addr < UB_UBAEND) {
ubapag_write(ub, addr, val);
return;
}
ub_badaddr("uba_write", addr, FALSE);
}
/* Write status reg.
** Note corresponding ubasta_read() is a macro.
*/
static void
ubasta_write(register struct ubctl *ub,
register h10_t val)
{
register h10_t clrbits, setbits;
/* Find bits to clear */
clrbits = (val & (UBA_BTIM|UBA_BBAD|UBA_BPAR|UBA_BNXD)) | UBA_BPWR
| (UBA_BDXF|UBA_BINI|UBA_BPIH|UBA_BPIL);
/* Find bits to set */
setbits = (val & (UBA_BDXF|UBA_BINI|UBA_BPIH|UBA_BPIL));
ub->ubsta = (ub->ubsta & ~clrbits) | setbits;
if (ub->ubsta & UBA_BINI) {
/* Do something to initialize this unibus?
** Note: ITS source says can't both init and set PI levels,
** hence the clearing done here for sake of emulation.
*/
val = (ub->ubsta &= ~(UBA_BINI|UBA_BPIH|UBA_BPIL));
}
ub->ubhilev = pilev_bits[(val & UBA_BPIH)>>3];
ub->ublolev = pilev_bits[val & UBA_BPIL];
}
/* Read paging regs.
** Note corresponding ubapag_write() is a macro
*/
static uint32
ubapag_read(register struct ubctl *ub,
dvuadr_t addr)
{
register uint32 wval;
register uint18 ret;
/* Reading, must get bits in screwy format */
ret = ub->ubpmap[addr - UB_UBAPAG];
wval = ((ret & (UBA_QRPW|UBA_Q16B|UBA_QFST|UBA_QVAL)) >> 5)
| UBA_PPVL /* Parity always valid */
| ((ret & UBA_QPAG) >> 9);
wval = (wval << 18) | (((ret & UBA_QPAG) << 9) & H10MASK);
return wval;
}
/* UBn_PIVGET - Called by PI system when responding to a PI request.
** Given a PI level mask (1 bit set),
** returns vector address for the "closest" device currently requesting
** an interrupt on that level. If none, returns 0.
*/
paddr_t
ub1_pivget(int lev)
{
register struct device **dp = dvub1.ubdevs;
/* Return first device wanting attention on given level. */
for (; *dp; dp++)
if (lev == (*dp)->dv_pireq)
return (paddr_t)(*(*dp)->dv_pivec)(*dp);
return 0;
}
paddr_t
ub3_pivget(int lev)
{
register struct device **dp = dvub3.ubdevs;
/* Return first device wanting attention on given level. */
for (; *dp; dp++)
if (lev == (*dp)->dv_pireq)
return (paddr_t)(*(*dp)->dv_pivec)(*dp);
return 0;
}
#endif /* KLH10_CPU_KS */