337 lines
12 KiB
C
337 lines
12 KiB
C
/* pdp18b_dr15.c: DR15C simulator
|
|
|
|
Copyright (c) 2016, 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
|
|
ROBERT M SUPNIK 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 of Robert M Supnik shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from Robert M Supnik.
|
|
|
|
dr PDP-15 DR15C interface for UC15 system
|
|
|
|
The DR15C provides control communications with the DR11Cs in the UC15.
|
|
Its state consists of an 18b register (the Task Control Block Pointer),
|
|
a one bit flag (TCBP acknowledge, not wired to interrupt), four interrupt
|
|
requests wired to API, four interrupt vectors for the API levels, and an
|
|
API interrupt enable/disable flag.
|
|
|
|
The PDP15 and UC15 use a master/save communications protocol.
|
|
- The PDP15 initiates a request to the PDP11 by writing TCBP and
|
|
clearing TCBP acknowledge. This alerts/interrupts the PDP11.
|
|
- The PDP11 reads TCBP. This sets TCBP acknowledge.
|
|
- The PDP11 processes the request.
|
|
- The PDP11 signals completion by writing a vector into one of
|
|
four API request levels.
|
|
- The PDP15 is interrupted, and the request is considered complete.
|
|
|
|
The DR15 must "call out" to the UC15 to signal two conditions:
|
|
- a new TCBP has been written
|
|
- API requests have been updated
|
|
|
|
The UC15 must "call in" to the DR15 for two reasons:
|
|
- the TCBP has been read
|
|
- an API interrupt is requested
|
|
|
|
The DR15 and UC15 use a shared memory section and ATOMIC operations
|
|
to communicate. Shared state is maintained in shared memory, with one
|
|
side having read/write access, the other read-only. Actions are
|
|
implemented by setting signals with an atomic compare-and-swap.
|
|
The signals may be polled with non-atomic operations but must be
|
|
verified with an atomic compare-and-swap.
|
|
|
|
Debug hooks - when DEBUG is turned on, the simulator will print
|
|
information relating to PIREX operation.
|
|
*/
|
|
|
|
#include "pdp18b_defs.h"
|
|
#include "uc15_defs.h"
|
|
|
|
/* Declarations */
|
|
|
|
extern int32 int_hwre[API_HLVL+1];
|
|
extern int32 api_vec[API_HLVL][32];
|
|
extern int32 *M;
|
|
extern UNIT cpu_unit;
|
|
|
|
uint32 dr15_tcbp = 0; /* buffer = TCB ptr */
|
|
int32 dr15_tcb_ack = 0; /* TCBP write ack */
|
|
int32 dr15_ie = 0; /* int enable */
|
|
uint32 dr15_int_req = 0; /* int req 0-3 */
|
|
int32 dr15_poll = 3; /* polling interval */
|
|
SHMEM *uc15_shmem = NULL; /* shared state identifier */
|
|
int32 *uc15_shstate = NULL; /* shared state base */
|
|
SHMEM *pdp15_shmem = NULL; /* PDP15 mem identifier */
|
|
|
|
int32 dr60 (int32 dev, int32 pulse, int32 AC);
|
|
int32 dr61 (int32 dev, int32 pulse, int32 AC);
|
|
t_stat dr15_reset (DEVICE *dptr);
|
|
void dr15_set_clr_ie (int32 val);
|
|
t_stat dr15_svc (UNIT *uptr);
|
|
t_stat dr15_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
|
|
t_stat dr15_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
|
|
t_stat dr15_attach (UNIT *uptr, CONST char *cptr);
|
|
t_stat dr15_detach (UNIT *uptr);
|
|
|
|
t_stat uc15_new_api (int32 val); /* callouts */
|
|
t_stat uc15_tcbp_wr (int32 val);
|
|
|
|
/* DR15 data structures
|
|
|
|
dr15_dev DR15 device descriptor
|
|
dr15_unit DR15 unit descriptor
|
|
dr15_reg DR15 register list
|
|
*/
|
|
|
|
DIB dr15_dib = { DEV_DR, 2 ,NULL, { &dr60, &dr61 } };
|
|
|
|
UNIT dr15_unit = {
|
|
UDATA (&dr15_svc, UNIT_FIX+UNIT_BINK+UNIT_ATTABLE, UC15_STATE_SIZE)
|
|
};
|
|
|
|
REG dr15_reg[] = {
|
|
{ ORDATA (TCBP, dr15_tcbp, ADDRSIZE) },
|
|
{ FLDATA (TCBACK, dr15_tcb_ack, 0) },
|
|
{ FLDATA (IE, dr15_ie, 0) },
|
|
{ ORDATA (REQ, dr15_int_req, 4) },
|
|
{ FLDATA (API0, int_hwre[API_DR0], INT_V_DR) },
|
|
{ FLDATA (API1, int_hwre[API_DR1], INT_V_DR) },
|
|
{ FLDATA (API2, int_hwre[API_DR2], INT_V_DR) },
|
|
{ FLDATA (API3, int_hwre[API_DR3], INT_V_DR) },
|
|
{ ORDATA (APIVEC0, api_vec[API_DR0][INT_V_DR], 7) },
|
|
{ ORDATA (APIVEC1, api_vec[API_DR1][INT_V_DR], 7) },
|
|
{ ORDATA (APIVEC2, api_vec[API_DR2][INT_V_DR], 7) },
|
|
{ ORDATA (APIVEC3, api_vec[API_DR3][INT_V_DR], 7) },
|
|
{ DRDATA (POLL, dr15_poll, 10), REG_NZ },
|
|
{ ORDATA (DEVNO, dr15_dib.dev, 6), REG_HRO },
|
|
{ NULL }
|
|
};
|
|
|
|
MTAB dr15_mod[] = {
|
|
{ MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", NULL, &show_devno },
|
|
{ 0 }
|
|
};
|
|
|
|
DEVICE dr15_dev = {
|
|
"DR", &dr15_unit, dr15_reg, dr15_mod,
|
|
1, 8, 10, 1, 8, 32,
|
|
&dr15_ex, &dr15_dep, &dr15_reset,
|
|
NULL, &dr15_attach, &dr15_detach,
|
|
&dr15_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG
|
|
};
|
|
|
|
/* IOT routines */
|
|
|
|
int32 dr60 (int32 dev, int32 pulse, int32 AC)
|
|
{
|
|
int32 subdev = (pulse >> 4) & 03;
|
|
|
|
if (((pulse & 01) != 0) && (dr15_tcb_ack != 0)) /* SIOA */
|
|
AC |= IOT_SKP;
|
|
if ((pulse & 02) != 0) /* CIOP */
|
|
dr15_tcb_ack = 0;
|
|
if ((pulse & 04) != 0) { /* LIOR */
|
|
dr15_tcbp = AC & AMASK; /* top bit zero */
|
|
uc15_tcbp_wr (dr15_tcbp); /* inform UC15 */
|
|
}
|
|
return AC;
|
|
}
|
|
|
|
int32 dr61 (int32 dev, int32 pulse, int32 AC)
|
|
{
|
|
int32 subdev = (pulse >> 4) & 03;
|
|
|
|
if (pulse & 01) { /* SAPIn */
|
|
if (((dr15_int_req >> subdev) & 01) != 0)
|
|
AC = AC | IOT_SKP;
|
|
}
|
|
if (pulse & 02) {
|
|
if (subdev == 0) /* RDRS */
|
|
AC |= dr15_ie;
|
|
else if (subdev == 1)
|
|
dr15_set_clr_ie (AC & 1);
|
|
}
|
|
if (pulse & 04) { /* CAPI */
|
|
int32 old_int_req = dr15_int_req;
|
|
dr15_int_req &= ~(1 << subdev); /* clear local req */
|
|
int_hwre[subdev] &= ~INT_DR; /* clear hwre req */
|
|
if (dr15_int_req != old_int_req) /* state change? */
|
|
uc15_new_api (dr15_int_req); /* inform UC15 */
|
|
}
|
|
return AC;
|
|
}
|
|
|
|
/* Set/clear interrupt enable */
|
|
|
|
void dr15_set_clr_ie (int32 val)
|
|
{
|
|
int32 i;
|
|
|
|
dr15_ie = val;
|
|
for (i = 0; i < 4; i++) {
|
|
if ((dr15_ie != 0) && (((dr15_int_req >> i) & 01) != 0))
|
|
int_hwre[i] |= INT_DR;
|
|
else int_hwre[i] &= ~INT_DR;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Routines to inform UC15 of state changes */
|
|
|
|
t_stat uc15_new_api (int32 req)
|
|
{
|
|
UC15_SHARED_WR (UC15_API_SUMM, req); /* new value */
|
|
UC15_ATOMIC_CAS (UC15_API_UPD, 0, 1); /* signal UC15 */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat uc15_tcbp_wr (int32 tcbp)
|
|
{
|
|
UC15_SHARED_WR (UC15_TCBP, tcbp); /* new value */
|
|
UC15_ATOMIC_CAS (UC15_TCBP_WR, 0, 1); /* signal UC15 */
|
|
if (DEBUG_PRS (dr15_dev)) {
|
|
uint32 apiv, apil, fnc, tsk;
|
|
t_bool spl;
|
|
|
|
apiv = (M[tcbp] >> 8) & 0377;
|
|
apil = M[tcbp] & 0377;
|
|
fnc = (M[tcbp + 1] >> 8) & 0377;
|
|
spl = (M[tcbp + 1] & 0200) != 0;
|
|
tsk = (M[tcbp + 1] & 0177);
|
|
fprintf (sim_deb, ">> DR15: TCB write, API = %o/%d, fnc = %o, %s task = %o, eventvar = %o\n",
|
|
apiv, apil, fnc, spl? "Spooled": "Unspooled", tsk, M[tcbp + 2]);
|
|
fprintf (sim_deb, "Additional parameters = %o %o %o %o %o\n",
|
|
M[tcbp + 3], M[tcbp + 4], M[tcbp + 5], M[tcbp + 6], M[tcbp + 7]);
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Routine to poll for state changes from UC15 */
|
|
|
|
t_stat dr15_svc (UNIT *uptr)
|
|
{
|
|
int32 i, t;
|
|
uint32 old_int_req = dr15_int_req;
|
|
|
|
t = UC15_SHARED_RD (UC15_TCBP_RD); /* TCBP read? */
|
|
if ((t != 0) && UC15_ATOMIC_CAS (UC15_TCBP_RD, 1, 0)) /* for real? clear */
|
|
dr15_tcb_ack = 1; /* set ack */
|
|
for (i = 0; i < 4; i++) { /* API req */
|
|
t = UC15_SHARED_RD (UC15_API_REQ + (i * UC15_API_VEC_MUL));
|
|
if ((t != 0) && /* API req? for real? */
|
|
UC15_ATOMIC_CAS (UC15_API_REQ + (i * UC15_API_VEC_MUL), 1, 0)) {
|
|
api_vec[i][INT_V_DR] = UC15_SHARED_RD (UC15_API_VEC + (i * UC15_API_VEC_MUL)) & 0177;
|
|
dr15_int_req |= (1u << i);
|
|
if (dr15_ie != 0)
|
|
int_hwre[i] |= INT_DR;
|
|
if (DEBUG_PRS (dr15_dev))
|
|
fprintf (sim_deb, ">>DR15: API request, API = %o/%d\n",
|
|
api_vec[i][INT_V_DR], i);
|
|
} /* end if changed */
|
|
} /* end for */
|
|
if (dr15_int_req != old_int_req) /* changes? */
|
|
uc15_new_api (dr15_int_req); /* inform UC15 */
|
|
sim_activate (uptr, dr15_poll); /* next poll */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Reset routine
|
|
|
|
Aside from performing a device reset, this routine sets up shared
|
|
UC15 state and shared PDP15 main memory. It also writes the size
|
|
of PDP15 main memory (in PDP11 bytes) into the shared state region.
|
|
*/
|
|
|
|
t_stat dr15_reset (DEVICE *dptr)
|
|
{
|
|
int32 i;
|
|
t_stat r;
|
|
void *basead;
|
|
|
|
dr15_int_req = 0; /* clear API req */
|
|
dr15_ie = 1; /* IE inits to 1 */
|
|
dr15_tcb_ack = 1; /* TCBP ack inits to 1 */
|
|
dr15_int_req = 0;
|
|
for (i = 0; i < 4; i++) { /* clear intr and vectors */
|
|
int_hwre[i] &= ~INT_DR;
|
|
api_vec[i][INT_V_DR] = 0;
|
|
}
|
|
sim_cancel (dptr->units);
|
|
if ((dptr->flags & DEV_DIS) != 0) /* disabled? */
|
|
return SCPE_OK;
|
|
|
|
if (uc15_shmem == NULL) { /* allocate shared state */
|
|
r = sim_shmem_open ("UC15SharedState", UC15_STATE_SIZE * sizeof (int32), &uc15_shmem, &basead);
|
|
if (r != SCPE_OK)
|
|
return r;
|
|
uc15_shstate = (int32 *) basead;
|
|
}
|
|
if (pdp15_shmem == NULL) { /* allocate shared memory */
|
|
r = sim_shmem_open ("PDP15MainMemory", MAXMEMSIZE * sizeof (int32), &pdp15_shmem, &basead);
|
|
if (r != SCPE_OK)
|
|
return r;
|
|
free (M); /* release normal memory */
|
|
M = (int32 *) basead;
|
|
}
|
|
UC15_SHARED_WR (UC15_PDP15MEM, cpu_unit.capac << 1); /* write mem size to shared state */
|
|
uc15_new_api (dr15_int_req); /* inform UC15 of new API (and mem) */
|
|
sim_activate (dptr->units, dr15_poll); /* start polling */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Shared state ex/mod routines for debug */
|
|
|
|
t_stat dr15_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
|
|
{
|
|
if (addr >= UC15_STATE_SIZE)
|
|
return SCPE_NXM;
|
|
if (vptr != NULL) {
|
|
if (uc15_shmem != NULL)
|
|
*vptr = UC15_SHARED_RD ((int32) addr);
|
|
else *vptr = 0;
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat dr15_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
|
|
{
|
|
if (addr >= UC15_STATE_SIZE)
|
|
return SCPE_NXM;
|
|
if (uc15_shmem != NULL)
|
|
UC15_SHARED_WR ((int32) addr, (int32) val);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Fake attach routine to kill attach attempts */
|
|
|
|
t_stat dr15_attach (UNIT *uptr, CONST char *cptr)
|
|
{
|
|
return SCPE_NOFNC;
|
|
}
|
|
|
|
/* Shutdown detach routine to release shared memories */
|
|
|
|
t_stat dr15_detach (UNIT *uptr)
|
|
{
|
|
if ((sim_switches & SIM_SW_SHUT) == 0) /* only if shutdown */
|
|
return SCPE_NOFNC;
|
|
sim_shmem_close (uc15_shmem); /* release shared state */
|
|
sim_shmem_close (pdp15_shmem); /* release shared mem */
|
|
return SCPE_OK;
|
|
}
|
|
|