/* 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; | |
} | |