blob: 4aacc1f0365e902bee7a15a2800aed915f49efda [file] [log] [blame] [raw]
/* 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;
}