blob: 03ad18df2d544d3beab153bd30f90a5819162b2b [file] [log] [blame] [raw]
/* 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 */
DEVICE dr15_dev;
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;
}