PDP11: TC11 - Revised to model TCCM correctly

As reported by Josh Dersch in #358

   Read hardware notes:

   - While the TCCM error bit is a real flop, it is supposed to reflect
     the OR of the TCST error bits at all time, so it is updated on read.
   - A read of TCDT while the function is RALL clears DONE.

Write hardware notes:

   - The TC11 behaves much more like a traditional DECtape controller
     than a typical PDP11 peripheral. In particular, execution is
     initiated/controlled by any write to TCCM, rather than setting
     the GO (DO) bit. Unless the function is STOP or STOP ALL, writing
     TCCM will put the selected tape in motion.
   - Writing GO (DO) clears DONE (READY) and the error flops in TCST.
   - Writing a 0 to ERROR clears the error flops in TCST. Because it
     is write 0 to clear (later controllers used write 1 to clear),
     the simulator has to know whether ERROR is actually written.
   - STOP ALL ignores select errors. Every other function is rejected
     if there is a select error.
   - An illegal operation (setting ILO) will stop the selected tape.
   - A write of TCDT while the function is RALL, WALL, or WTMK clears
     DONE (READY). RALL should not be included, but it saved a gate
     not to prevent it.
   - Because DONE (READY) may not be clear when an operation completes
     and DONE (READY) is set, the DT_SETDONE must test for DONE (READY)
     not being already set.
This commit is contained in:
Mark Pizzolato 2016-12-04 11:08:23 -08:00
parent 9a69fa8b49
commit 0a0bad9139

View file

@ -1,6 +1,6 @@
/* pdp11_tc.c: PDP-11 DECtape simulator
Copyright (c) 1993-2013, Robert M Supnik
Copyright (c) 1993-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"),
@ -25,6 +25,7 @@
tc TC11/TU56 DECtape
04-Dec-13 RMS Revised to model TCCM correctly (Josh Dersch)
23-Oct-13 RMS Revised for new boot setup routine
23-Jun-06 RMS Fixed switch conflict in ATTACH
10-Feb-06 RMS READ sets extended data bits in TCST (Alan Frisbie)
@ -274,11 +275,11 @@ extern int32 int_req[IPL_HLVL];
#define LOG_RW 0x2
#define LOG_BL 0x4
#define DT_SETDONE tccm = tccm | CSR_DONE; \
if (tccm & CSR_IE) \
SET_INT (DTA)
#define DT_CLRDONE tccm = tccm & ~CSR_DONE; \
CLR_INT (DTA)
#define DT_SETDONE if ((tccm & (CSR_DONE|CSR_IE)) == CSR_IE) \
SET_INT (DTA); \
tccm = tccm | CSR_DONE
#define DT_CLRDONE CLR_INT (DTA); \
tccm = tccm & ~CSR_DONE
#define ABS(x) (((x) < 0)? (-(x)): (x))
int32 tcst = 0; /* status */
@ -411,7 +412,14 @@ DEVICE dt_dev = {
&dt_description
};
/* IO dispatch routines, I/O addresses 17777340 - 17777350 */
/* IO dispatch routines, I/O addresses 17777340 - 17777350
Read hardware notes:
- While the TCCM error bit is a real flop, it is supposed to reflect
the OR of the TCST error bits at all time, so it is updated on read.
- A read of TCDT while the function is RALL clears DONE.
*/
t_stat dt_rd (int32 *data, int32 PA, int32 access)
{
@ -456,6 +464,28 @@ switch (j) {
return SCPE_OK;
}
/* Write hardware notes:
- The TC11 behaves much more like a traditional DECtape controller
than a typical PDP11 peripheral. In particular, execution is
initiated/controlled by any write to TCCM, rather than setting
the GO (DO) bit. Unless the function is STOP or STOP ALL, writing
TCCM will put the selected tape in motion.
- Writing GO (DO) clears DONE (READY) and the error flops in TCST.
- Writing a 0 to ERROR clears the error flops in TCST. Because it
is write 0 to clear (later controllers used write 1 to clear),
the simulator has to know whether ERROR is actually written.
- STOP ALL ignores select errors. Every other function is rejected
if there is a select error.
- An illegal operation (setting ILO) will stop the selected tape.
- A write of TCDT while the function is RALL, WALL, or WTMK clears
DONE (READY). RALL should not be included, but it saved a gate
not to prevent it.
- Because DONE (READY) may not be clear when an operation completes
and DONE (READY) is set, the DT_SETDONE must test for DONE (READY)
not being already set.
*/
t_stat dt_wr (int32 data, int32 PA, int32 access)
{
int32 i, j, unum, old_tccm, fnc;
@ -473,23 +503,28 @@ switch (j) {
case 001: /* TCCM */
old_tccm = tccm; /* save prior */
if (access == WRITEB)
data = (PA & 1)? (tccm & 0377) | (data << 8): (tccm & ~0377) | data;
if ((data & CSR_IE) == 0)
CLR_INT (DTA);
else if ((((tccm & CSR_IE) == 0) && (tccm & CSR_DONE)) ||
(data & CSR_DONE)) SET_INT (DTA);
tccm = (tccm & ~CSR_RW) | (data & CSR_RW);
if ((data & CSR_GO) && (tccm & CSR_DONE)) { /* new cmd? */
data = (PA & 1)? ((tccm & 0377) | (data << 8)): ((tccm & ~0377) | data);
if ((data & CSR_IE) == 0) /* clearing IE? */
CLR_INT (DTA); /* clear intr */
else if ((tccm & (CSR_DONE|CSR_IE)) == CSR_DONE) /* setting, DON'IE = DON? */
SET_INT (DTA); /* set intr */
tccm = (tccm & ~CSR_RW) | (data & CSR_RW); /* merge data */
if ((data & CSR_GO) != 0) { /* GO (DO) set? */
tcst = tcst & ~STA_ALLERR; /* clear errors */
tccm = tccm & ~(CSR_ERR | CSR_DONE); /* clear done, err */
CLR_INT (DTA); /* clear int */
if ((old_tccm ^ tccm) & CSR_UNIT)
dt_deselect (old_tccm);
tccm = tccm & ~(CSR_ERR|CSR_DONE); /* clear done, err flops */
}
else if (((data & CSR_ERR) == 0) && /* error bit clear? */
((access != WRITEB) || ((PA & 1) != 0))) { /* not write low byte? */
tcst = tcst & ~STA_ALLERR; /* clear errors */
tccm = tccm & ~CSR_ERR; /* clear err flop */
}
if (((old_tccm ^ tccm) & CSR_UNIT) != 0) /* unit change? */
dt_deselect (old_tccm); /* deselect all */
unum = CSR_GETUNIT (tccm); /* get drive */
fnc = CSR_GETFNC (tccm); /* get function */
if (fnc == FNC_STOP) { /* stop all? */
sim_activate (&dt_dev.units[DT_TIMER], dt_ctime);
for (i = 0; i < DT_NUMDR; i++)
sim_activate (&dt_dev.units[DT_TIMER], dt_ctime); /* sched done */
for (i = 0; i < DT_NUMDR; i++) /* loop thru units */
dt_stopunit (dt_dev.units + i); /* stop unit */
break;
}
@ -500,14 +535,11 @@ switch (j) {
((fnc == FNC_WALL) && (uptr->flags & UNIT_WPRT)) ||
((fnc == FNC_WRIT) && (uptr->flags & UNIT_WPRT)))
dt_seterr (uptr, STA_ILO); /* illegal op */
if (!(tccm & CSR_ERR))
dt_newsa (tccm);
}
else if ((tccm & CSR_ERR) == 0) { /* clear err? */
tcst = tcst & ~STA_RWERR;
if (tcst & STA_ALLERR)
tccm = tccm | CSR_ERR;
if ((tccm & CSR_ERR) != 0) { /* error? */
dt_stopunit (uptr); /* stop the unit */
DT_SETDONE; /* set done at once */
}
else dt_newsa (tccm); /* new function */
break;
case 002: /* TCWC */