267 lines
9.2 KiB
C
267 lines
9.2 KiB
C
/* pdp18b_rf.c: fixed head disk simulator
|
||
|
||
Copyright (c) 1993-2001, 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.
|
||
|
||
rf (PDP-9) RF09/RF09
|
||
(PDP-15) RF15/RS09
|
||
|
||
26-Apr-01 RMS Added device enable/disable support
|
||
15-Feb-01 RMS Fixed 3 cycle data break sequencing
|
||
30-Nov-99 RMS Added non-zero requirement to rf_time
|
||
14-Apr-99 RMS Changed t_addr to unsigned
|
||
|
||
The RFxx is a head-per-track disk. It uses the multicycle data break
|
||
facility. To minimize overhead, the entire RFxx is buffered in memory.
|
||
|
||
Two timing parameters are provided:
|
||
|
||
rf_time Interword timing. Must be non-zero.
|
||
rf_burst Burst mode. If 0, DMA occurs cycle by cycle; otherwise,
|
||
DMA occurs in a burst.
|
||
*/
|
||
|
||
#include "pdp18b_defs.h"
|
||
#include <math.h>
|
||
|
||
/* Constants */
|
||
|
||
#define RF_NUMWD 2048 /* words/track */
|
||
#define RF_NUMTR 128 /* tracks/disk */
|
||
#define RF_NUMDK 8 /* disks/controller */
|
||
#define RF_SIZE (RF_NUMDK * RF_NUMTR * RF_NUMWD) /* words/drive */
|
||
#define RF_WMASK (RF_NUMWD - 1) /* word mask */
|
||
#define RF_WC 036 /* word count */
|
||
#define RF_CA 037 /* current addr */
|
||
|
||
/* Function/status register */
|
||
|
||
#define RFS_ERR 0400000 /* error */
|
||
#define RFS_HDW 0200000 /* hardware error */
|
||
#define RFS_APE 0100000 /* addr parity error */
|
||
#define RFS_MXF 0040000 /* missed transfer */
|
||
#define RFS_WCE 0020000 /* write check error */
|
||
#define RFS_DPE 0010000 /* data parity error */
|
||
#define RFS_WLO 0004000 /* write lock error */
|
||
#define RFS_NED 0002000 /* non-existent disk */
|
||
#define RFS_DCH 0001000 /* data chan timing */
|
||
#define RFS_PGE 0000400 /* programming error */
|
||
#define RFS_DON 0000200 /* transfer complete */
|
||
#define RFS_V_FNC 1 /* function */
|
||
#define RFS_M_FNC 03
|
||
#define RFS_FNC (RFS_M_FNC << RFS_V_FNC)
|
||
#define FN_NOP 0
|
||
#define FN_READ 1
|
||
#define FN_WRITE 2
|
||
#define FN_WCHK 3
|
||
#define RFS_IE 0000001 /* interrupt enable */
|
||
|
||
#define RFS_CLR 0000170 /* always clear */
|
||
#define RFS_EFLGS (RFS_HDW | RFS_APE | RFS_MXF | RFS_WCE | \
|
||
RFS_DPE | RFS_WLO | RFS_NED ) /* error flags */
|
||
#define GET_FNC(x) (((x) >> RFS_V_FNC) & RFS_M_FNC)
|
||
#define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \
|
||
((double) RF_NUMWD)))
|
||
#define RF_BUSY (sim_is_active (&rf_unit))
|
||
|
||
extern int32 M[];
|
||
extern int32 int_req, dev_enb;
|
||
extern UNIT cpu_unit;
|
||
int32 rf_sta = 0; /* status register */
|
||
int32 rf_da = 0; /* disk address */
|
||
int32 rf_dbuf = 0; /* data buffer */
|
||
int32 rf_wlk[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; /* write lock */
|
||
int32 rf_time = 10; /* inter-word time */
|
||
int32 rf_burst = 1; /* burst mode flag */
|
||
int32 rf_stopioe = 1; /* stop on error */
|
||
t_stat rf_svc (UNIT *uptr);
|
||
t_stat rf_reset (DEVICE *dptr);
|
||
int32 rf_updsta (int32 new);
|
||
|
||
/* RF data structures
|
||
|
||
rf_dev RF device descriptor
|
||
rf_unit RF unit descriptor
|
||
rf_reg RF register list
|
||
*/
|
||
|
||
UNIT rf_unit =
|
||
{ UDATA (&rf_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF,
|
||
RF_SIZE) };
|
||
|
||
REG rf_reg[] = {
|
||
{ ORDATA (STA, rf_sta, 18) },
|
||
{ ORDATA (DA, rf_da, 21) },
|
||
{ ORDATA (WC, M[RF_WC], 18) },
|
||
{ ORDATA (CA, M[RF_CA], 18) },
|
||
{ ORDATA (BUF, rf_dbuf, 18) },
|
||
{ FLDATA (INT, int_req, INT_V_RF) },
|
||
{ ORDATA (WLK0, rf_wlk[0], 16) },
|
||
{ ORDATA (WLK1, rf_wlk[1], 16) },
|
||
{ ORDATA (WLK2, rf_wlk[2], 16) },
|
||
{ ORDATA (WLK3, rf_wlk[3], 16) },
|
||
{ ORDATA (WLK4, rf_wlk[4], 16) },
|
||
{ ORDATA (WLK5, rf_wlk[5], 16) },
|
||
{ ORDATA (WLK6, rf_wlk[6], 16) },
|
||
{ ORDATA (WLK7, rf_wlk[7], 16) },
|
||
{ DRDATA (TIME, rf_time, 24), PV_LEFT + REG_NZ },
|
||
{ FLDATA (BURST, rf_burst, 0) },
|
||
{ FLDATA (STOP_IOE, rf_stopioe, 0) },
|
||
{ FLDATA (*DEVENB, dev_enb, INT_V_RF), REG_HRO },
|
||
{ NULL } };
|
||
|
||
DEVICE rf_dev = {
|
||
"RF", &rf_unit, rf_reg, NULL,
|
||
1, 8, 21, 1, 8, 18,
|
||
NULL, NULL, &rf_reset,
|
||
NULL, NULL, NULL };
|
||
|
||
/* IOT routines */
|
||
|
||
int32 rf70 (int32 pulse, int32 AC)
|
||
{
|
||
int32 t;
|
||
|
||
if (pulse == 001) /* DSSF */
|
||
return (rf_sta & (RFS_ERR | RFS_DON))? IOT_SKP + AC: AC;
|
||
if (pulse == 021) rf_reset (&rf_dev); /* DSCC */
|
||
if ((pulse & 061) == 041) { /* DSCF */
|
||
if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy inhibits */
|
||
else rf_sta = rf_sta & ~(RFS_FNC | RFS_IE); } /* clear func */
|
||
if (pulse == 002) { /* DRBR */
|
||
if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy sets PGE */
|
||
return AC | rf_dbuf; }
|
||
if (pulse == 022) { /* DRAL */
|
||
if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy sets PGE */
|
||
return rf_da & 0777777; }
|
||
if (pulse == 062) { /* DRAH */
|
||
if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy sets PGE */
|
||
return (rf_da >> 18) | ((rf_sta & RFS_NED)? 010: 0); }
|
||
if ((pulse & 062) == 042) { /* DSFX */
|
||
if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy inhibits */
|
||
else rf_sta = rf_sta ^ (AC & (RFS_FNC | RFS_IE)); } /* xor func */
|
||
if (pulse == 004) { /* DLBR */
|
||
if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy inhibits */
|
||
else rf_dbuf = AC; }
|
||
if (pulse == 024) { /* DLAL */
|
||
if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy inhibits */
|
||
else rf_da = (rf_da & ~0777777) | AC; }
|
||
if (pulse == 064) { /* DLAH */
|
||
if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy inhibits */
|
||
else rf_da = (rf_da & 0777777) | ((AC & 07) << 18); }
|
||
if ((pulse & 064) == 044) { /* DSCN */
|
||
if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy inhibits */
|
||
else if (GET_FNC (rf_sta) != FN_NOP) {
|
||
t = (rf_da & RF_WMASK) - GET_POS (rf_time); /* delta to new */
|
||
if (t < 0) t = t + RF_NUMWD; /* wrap around? */
|
||
sim_activate (&rf_unit, t * rf_time); } } /* schedule op */
|
||
rf_updsta (0); /* update status */
|
||
return AC;
|
||
}
|
||
|
||
int32 rf72 (int32 pulse, int32 AC)
|
||
{
|
||
if (pulse == 002) return AC | GET_POS (rf_time) | /* DLOK */
|
||
(sim_is_active (&rf_unit)? 0400000: 0);
|
||
if (pulse == 042) { /* DSCD */
|
||
if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy inhibits */
|
||
else rf_sta = 0;
|
||
rf_updsta (0); }
|
||
if (pulse == 0062) { /* DSRS */
|
||
if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy sets PGE */
|
||
return rf_updsta (0); }
|
||
return AC;
|
||
}
|
||
|
||
/* Unit service
|
||
|
||
This code assumes the entire disk is buffered.
|
||
*/
|
||
|
||
t_stat rf_svc (UNIT *uptr)
|
||
{
|
||
int32 f, pa, d, t;
|
||
|
||
if ((uptr -> flags & UNIT_BUF) == 0) { /* not buf? abort */
|
||
rf_updsta (RFS_NED | RFS_DON); /* set nxd, done */
|
||
return IORETURN (rf_stopioe, SCPE_UNATT); }
|
||
|
||
f = GET_FNC (rf_sta); /* get function */
|
||
do { M[RF_WC] = (M[RF_WC] + 1) & 0777777; /* incr word count */
|
||
pa = M[RF_CA] = (M[RF_CA] + 1) & ADDRMASK; /* incr mem addr */
|
||
if ((f == FN_READ) && MEM_ADDR_OK (pa)) /* read? */
|
||
M[pa] = *(((int32 *) uptr -> filebuf) + rf_da);
|
||
if ((f == FN_WCHK) && /* write check? */
|
||
(M[pa] != *(((int32 *) uptr -> filebuf) + rf_da))) {
|
||
rf_updsta (RFS_WCE); /* flag error */
|
||
break; }
|
||
if (f == FN_WRITE) { /* write? */
|
||
d = (rf_da >> 18) & 07; /* disk */
|
||
t = (rf_da >> 14) & 017; /* track groups */
|
||
if ((rf_wlk[d] >> t) & 1) { /* write locked? */
|
||
rf_updsta (RFS_WLO);
|
||
break; }
|
||
else { *(((int32 *) uptr -> filebuf) + rf_da) = M[pa];
|
||
if (((t_addr) rf_da) >= uptr -> hwmark)
|
||
uptr -> hwmark = rf_da + 1; } }
|
||
rf_da = rf_da + 1; /* incr disk addr */
|
||
if (rf_da > RF_SIZE) { /* disk overflow? */
|
||
rf_da = 0;
|
||
rf_updsta (RFS_NED); /* nx disk error */
|
||
break; } }
|
||
while ((M[RF_WC] != 0) && (rf_burst != 0)); /* brk if wc, no brst */
|
||
|
||
if ((M[RF_WC] != 0) && ((rf_sta & RFS_ERR) == 0)) /* more to do? */
|
||
sim_activate (&rf_unit, rf_time); /* sched next */
|
||
else rf_updsta (RFS_DON);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Update status */
|
||
|
||
int32 rf_updsta (int32 new)
|
||
{
|
||
rf_sta = (rf_sta | new) & ~(RFS_ERR | RFS_CLR);
|
||
if (rf_sta & RFS_EFLGS) rf_sta = rf_sta | RFS_ERR;
|
||
if ((rf_sta & (RFS_ERR | RFS_DON)) && (rf_sta & RFS_IE))
|
||
int_req = int_req | INT_RF;
|
||
else int_req = int_req & ~INT_RF;
|
||
return rf_sta;
|
||
}
|
||
|
||
/* Reset routine */
|
||
|
||
t_stat rf_reset (DEVICE *dptr)
|
||
{
|
||
rf_sta = rf_da = rf_dbuf = 0;
|
||
rf_updsta (0);
|
||
sim_cancel (&rf_unit);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* IORS routine */
|
||
|
||
int32 rf_iors (void)
|
||
{
|
||
return ((rf_sta & (RFS_ERR | RFS_DON))? IOS_RF: 0);
|
||
}
|