/* sds_drm.c: SDS 940 Project Genie drum simulator | |
Copyright (c) 2002-2017, 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. | |
drm drum | |
13-Mar-17 RMS Annotated fall through in switch | |
03-Sep-13 RMS Added explicit void * cast | |
The drum is buffered in memory. | |
Note: the Project Genie documentation and the actual monitor sources disagree | |
on the I/O instruction definitions for the drum. The simulator follows the | |
monitor sources, as follows: | |
DCC OP 00230404B RESET DRUM CHANNEL | |
DSC OP 00230204B START DRUM CHANNEL (NO CHAIN) | |
DRA OP 00230504B READ DRUM TIMING COUNTER INTO 21B | |
DSR OP 04030204B SKIP IF DRUM NOT BUSY | |
DSE OP 04037404B SKIP IF NO DRUM ERROR | |
*/ | |
#include "sds_defs.h" | |
#include <math.h> | |
/* Constants */ | |
#define DRM_N_WD 11 /* word addr width */ | |
#define DRM_V_WD 0 /* position */ | |
#define DRM_M_WD ((1 << DRM_N_WD) - 1) /* word mask */ | |
#define DRM_NUMWD (1 << DRM_N_WD) /* words/sector */ | |
#define DRM_NUMGP 236 /* gap/sector */ | |
#define DRM_PHYWD (DRM_NUMWD + DRM_NUMGP) /* phys wds/sector */ | |
#define DRM_N_SC 3 /* sect addr width */ | |
#define DRM_V_SC (DRM_N_WD) /* position */ | |
#define DRM_M_SC ((1 << DRM_N_SC) - 1) /* sector mask */ | |
#define DRM_NUMSC (1 << DRM_N_SC) /* sectors/track */ | |
#define DRM_N_TR 7 /* track addr width */ | |
#define DRM_V_TR (DRM_N_WD+DRM_N_SC) /* position */ | |
#define DRM_M_TR ((1 << DRM_N_TR) - 1) /* track mask */ | |
#define DRM_NUMTR 84 /* tracks/drum */ | |
#define DRM_N_ADDR (DRM_N_WD+DRM_N_SC+DRM_N_TR) /* drum addr width */ | |
#define DRM_SWMASK ((1 << (DRM_N_WD+DRM_N_SC)) - 1)/* sector+word mask */ | |
#define DRM_DAMASK ((1 << DRM_N_ADDR) - 1) /* drum addr mask */ | |
#define DRM_SIZE (DRM_NUMTR*DRM_NUMSC*DRM_NUMWD) /* words/disk */ | |
#define DRM_WCMASK 037777 /* wc mask */ | |
#define DRM_GETSC(x) (((x) >> DRM_V_SC) & DRM_M_SC) | |
#define DRM_PC 020 | |
#define DRM_AD 021 | |
#define DRM_ADAT (1 << (DRM_N_WD + DRM_N_SC)) /* data flag */ | |
#define DRM_SFET 0 /* fetch state */ | |
#define DRM_SFCA 1 /* fetch CA */ | |
#define DRM_SFDA 2 /* fetch DA */ | |
#define DRM_SXFR 3 /* xfer */ | |
#define DRM_V_OP 21 /* drum op */ | |
#define DRM_M_OP 07 | |
#define DRM_V_RW 20 | |
#define DRM_GETOP(x) (((x) >> DRM_V_OP) & DRM_M_OP) | |
#define DRM_GETRW(x) (((x) >> DRM_V_RW) & 1) | |
#define DRM_OXF 0 /* xfer */ | |
#define DRM_OCX 1 /* cond xfer */ | |
#define DRM_OBR 2 /* branch */ | |
#define DRM_ORS 3 /* reset error */ | |
#define DRM_END 4 /* end prog */ | |
#define DRM_EIE 5 /* end int if err */ | |
#define DRM_EIU 7 /* end int uncond */ | |
#define GET_TWORD(x) ((int32) fmod (sim_gtime() / ((double) (x)), \ | |
((double) (DRM_NUMSC * DRM_PHYWD)))) | |
extern uint32 M[]; /* memory */ | |
extern uint32 alert, int_req; | |
extern int32 stop_invins, stop_invdev, stop_inviop; | |
uint32 drm_da = 0; /* disk address */ | |
uint32 drm_ca = 0; /* core address */ | |
uint32 drm_wc = 0; /* word count */ | |
int32 drm_par = 0; /* cumulative par */ | |
int32 drm_err = 0; /* error */ | |
int32 drm_rw = 0; /* read/write */ | |
int32 drm_sta = 0; /* drum state */ | |
int32 drm_ftime = 3; /* time to fetch */ | |
int32 drm_xtime = 1; /* time to xfr */ | |
int32 drm_stopioe = 1; /* stop on error */ | |
t_stat drm (uint32 fnc, uint32 inst, uint32 *dat); | |
t_stat drm_svc (UNIT *uptr); | |
t_stat drm_reset (DEVICE *dptr); | |
/* DRM data structures | |
drm_dev device descriptor | |
drm_unit unit descriptor | |
drm_reg register list | |
*/ | |
DIB drm_dib = { -1, DEV3_GDRM, 0, NULL, &drm }; | |
UNIT drm_unit = { | |
UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, | |
DRM_SIZE) | |
}; | |
REG drm_reg[] = { | |
{ ORDATA (DA, drm_da, DRM_N_ADDR) }, | |
{ ORDATA (CA, drm_ca, 16) }, | |
{ ORDATA (WC, drm_wc, 14) }, | |
{ ORDATA (PAR, drm_par, 12) }, | |
{ FLDATA (RW, drm_rw, 0) }, | |
{ FLDATA (ERR, drm_err, 0) }, | |
{ ORDATA (STA, drm_sta, 2) }, | |
{ DRDATA (FTIME, drm_ftime, 24), REG_NZ + PV_LEFT }, | |
{ DRDATA (XTIME, drm_xtime, 24), REG_NZ + PV_LEFT }, | |
{ FLDATA (STOP_IOE, drm_stopioe, 0) }, | |
{ NULL } | |
}; | |
DEVICE drm_dev = { | |
"DRM", &drm_unit, drm_reg, NULL, | |
1, 8, DRM_N_ADDR, 1, 8, 24, | |
NULL, NULL, &drm_reset, | |
NULL, NULL, NULL, | |
&drm_dib, DEV_DISABLE | DEV_DIS | |
}; | |
/* Drum routine - EOM/SKS 3xx04 */ | |
t_stat drm (uint32 fnc, uint32 inst, uint32 *dat) | |
{ | |
int32 t, op = inst & 07700; | |
switch (fnc) { | |
case IO_CONN: /* connect */ | |
if (op == 00400) /* EOM 404 = reset */ | |
return drm_reset (&drm_dev); | |
if (op == 00500) { /* EOM 504 = read DA */ | |
if (sim_is_active (&drm_unit)) | |
return SCPE_OK; /* must be idle */ | |
t = GET_TWORD (drm_xtime); /* get position */ | |
if (t < DRM_NUMGP) /* in gap? */ | |
M[DRM_AD] = DRM_NUMWD - t; | |
else M[DRM_AD] = (t - DRM_NUMGP) | DRM_ADAT;/* in data */ | |
} | |
else if (op == 00200) { /* EOM 204 = start */ | |
if (sim_is_active (&drm_unit)) /* must be idle */ | |
return SCPE_OK; | |
drm_sta = DRM_SFET; /* state = fetch */ | |
sim_activate (&drm_unit, drm_ftime); /* activate */ | |
} | |
else CRETINS; | |
break; | |
case IO_SKS: /* SKS */ | |
if (((op == 07400) && !drm_err) || /* 37404: no err */ | |
((op == 00200) && !sim_is_active (&drm_unit))) /* 30204: idle */ | |
*dat = 1; | |
break; | |
default: | |
return SCPE_IERR; | |
} | |
return SCPE_OK; | |
} | |
/* Unit service */ | |
t_stat drm_svc (UNIT *uptr) | |
{ | |
int32 t, rda; | |
uint32 dpc, dwd; | |
uint32 *fbuf = (uint32 *) uptr->filebuf; | |
if (drm_sta != DRM_SXFR) { /* fetch drum prog? */ | |
dpc = M[DRM_PC]; /* get drum PC */ | |
dwd = M[dpc & PAMASK]; /* get drum inst */ | |
M[DRM_PC] = (dpc + 1) & PAMASK; /* update drum PC */ | |
if (drm_sta == DRM_SFCA) { /* fetch core addr? */ | |
drm_rw = DRM_GETRW (dwd); /* set op */ | |
drm_ca = dwd & PAMASK; /* set core addr */ | |
drm_sta = DRM_SFDA; /* next is disk addr */ | |
} | |
else if (drm_sta == DRM_SFDA) { /* fetch disk addr? */ | |
drm_da = dwd & DRM_DAMASK; /* set disk addr */ | |
drm_sta = DRM_SXFR; /* next is xfer */ | |
drm_par = 0; /* init parity */ | |
rda = (drm_da & DRM_SWMASK) + (DRM_GETSC (drm_da) * DRM_NUMGP); | |
t = rda - GET_TWORD (drm_xtime); /* difference */ | |
if (t <= 0) /* add trk lnt */ | |
t = t + (DRM_NUMSC * DRM_PHYWD); | |
sim_activate (&drm_unit, t * drm_xtime); /* activate */ | |
} | |
else { | |
switch (DRM_GETOP (dwd)) { | |
case DRM_OCX: /* cond xfr */ | |
if (drm_err) { /* error? */ | |
int_req = int_req | INT_DRM; /* req int */ | |
return SCPE_OK; /* done */ | |
} | |
case DRM_OXF: /* transfer */ | |
drm_wc = dwd & DRM_WCMASK; /* save wc */ | |
drm_sta = DRM_SFCA; /* next state */ | |
break; | |
case DRM_OBR: /* branch */ | |
M[DRM_PC] = dwd & PAMASK; /* new drum PC */ | |
break; | |
case DRM_END: /* end */ | |
return SCPE_OK; | |
case DRM_EIE: /* end, int if err */ | |
if (!drm_err) | |
return SCPE_OK; | |
/* fall through */ | |
case DRM_EIU: /* end, int uncond */ | |
int_req = int_req | INT_DRM; | |
return SCPE_OK; | |
} /* end switch */ | |
} /* end else sta */ | |
sim_activate (uptr, drm_ftime); /* fetch next word */ | |
} /* end if !xfr */ | |
else { /* transfer word */ | |
if ((uptr->flags & UNIT_BUF) == 0) { /* not buffered? */ | |
drm_err = 1; /* error */ | |
CRETIOE (drm_stopioe, SCPE_UNATT); | |
} | |
if (drm_rw) { /* write? */ | |
dwd = M[drm_ca]; /* get mem word */ | |
fbuf[drm_da] = dwd; /* write to drum */ | |
if (drm_da >= uptr->hwmark) | |
uptr->hwmark = drm_da + 1; | |
} | |
else { /* read */ | |
dwd = fbuf[drm_da]; /* get drum word */ | |
M[drm_ca] = dwd; /* write to mem */ | |
} | |
drm_da = drm_da + 1; /* inc drum addr */ | |
if (drm_da >= DRM_SIZE) /* wrap */ | |
drm_da = 0; | |
drm_ca = (drm_ca + 1) & PAMASK; /* inc core addr */ | |
drm_wc = (drm_wc - 1) & DRM_WCMASK; /* dec word cnt */ | |
drm_par = drm_par ^ (dwd >> 12); /* parity */ | |
drm_par = ((drm_par << 1) | (drm_par >> 11)) & 07777; | |
drm_par = drm_par ^ (dwd & 07777); | |
if (drm_wc) { /* more to do */ | |
if (drm_da & DRM_M_WD) | |
sim_activate (uptr, drm_xtime); | |
else sim_activate (uptr, drm_xtime * DRM_NUMGP); | |
} | |
else { /* end xfr */ | |
#if defined (DRM_PAR) | |
if ((drm_da & DRM_M_WD) && drm_rw) { /* wr end mid sector? */ | |
M[drm_da] = drm_par << 12; /* clobber data */ | |
if (drm_da >= uptr->hwmark) | |
uptr->hwmark = drm_da + 1; | |
} | |
#endif | |
drm_sta = DRM_SFET; /* back to fetch */ | |
sim_activate (uptr, drm_ftime); /* schedule */ | |
} /* end else end xfr */ | |
} /* end else xfr */ | |
return SCPE_OK; | |
} | |
/* Reset routine */ | |
t_stat drm_reset (DEVICE *dptr) | |
{ | |
drm_da = 0; /* clear state */ | |
drm_ca = 0; | |
drm_wc = 0; | |
drm_par = 0; | |
drm_sta = 0; | |
drm_err = 0; | |
drm_rw = 0; | |
int_req = int_req & ~INT_DRM; /* clear intr */ | |
sim_cancel (&drm_unit); /* deactivate */ | |
return SCPE_OK; | |
} |