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 /* 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 Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"), copy of this software and associated documentation files (the "Software"),
@ -25,6 +25,7 @@
tc TC11/TU56 DECtape 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-Oct-13 RMS Revised for new boot setup routine
23-Jun-06 RMS Fixed switch conflict in ATTACH 23-Jun-06 RMS Fixed switch conflict in ATTACH
10-Feb-06 RMS READ sets extended data bits in TCST (Alan Frisbie) 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_RW 0x2
#define LOG_BL 0x4 #define LOG_BL 0x4
#define DT_SETDONE tccm = tccm | CSR_DONE; \ #define DT_SETDONE if ((tccm & (CSR_DONE|CSR_IE)) == CSR_IE) \
if (tccm & CSR_IE) \ SET_INT (DTA); \
SET_INT (DTA) tccm = tccm | CSR_DONE
#define DT_CLRDONE tccm = tccm & ~CSR_DONE; \ #define DT_CLRDONE CLR_INT (DTA); \
CLR_INT (DTA) tccm = tccm & ~CSR_DONE
#define ABS(x) (((x) < 0)? (-(x)): (x)) #define ABS(x) (((x) < 0)? (-(x)): (x))
int32 tcst = 0; /* status */ int32 tcst = 0; /* status */
@ -411,7 +412,14 @@ DEVICE dt_dev = {
&dt_description &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) t_stat dt_rd (int32 *data, int32 PA, int32 access)
{ {
@ -456,6 +464,28 @@ switch (j) {
return SCPE_OK; 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) t_stat dt_wr (int32 data, int32 PA, int32 access)
{ {
int32 i, j, unum, old_tccm, fnc; int32 i, j, unum, old_tccm, fnc;
@ -473,42 +503,44 @@ switch (j) {
case 001: /* TCCM */ case 001: /* TCCM */
old_tccm = tccm; /* save prior */ old_tccm = tccm; /* save prior */
if (access == WRITEB) if (access == WRITEB)
data = (PA & 1)? (tccm & 0377) | (data << 8): (tccm & ~0377) | data; data = (PA & 1)? ((tccm & 0377) | (data << 8)): ((tccm & ~0377) | data);
if ((data & CSR_IE) == 0) if ((data & CSR_IE) == 0) /* clearing IE? */
CLR_INT (DTA); CLR_INT (DTA); /* clear intr */
else if ((((tccm & CSR_IE) == 0) && (tccm & CSR_DONE)) || else if ((tccm & (CSR_DONE|CSR_IE)) == CSR_DONE) /* setting, DON'IE = DON? */
(data & CSR_DONE)) SET_INT (DTA); SET_INT (DTA); /* set intr */
tccm = (tccm & ~CSR_RW) | (data & CSR_RW); tccm = (tccm & ~CSR_RW) | (data & CSR_RW); /* merge data */
if ((data & CSR_GO) && (tccm & CSR_DONE)) { /* new cmd? */ if ((data & CSR_GO) != 0) { /* GO (DO) set? */
tcst = tcst & ~STA_ALLERR; /* clear errors */ tcst = tcst & ~STA_ALLERR; /* clear errors */
tccm = tccm & ~(CSR_ERR | CSR_DONE); /* clear done, err */ tccm = tccm & ~(CSR_ERR|CSR_DONE); /* clear done, err flops */
CLR_INT (DTA); /* clear int */
if ((old_tccm ^ tccm) & CSR_UNIT)
dt_deselect (old_tccm);
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++)
dt_stopunit (dt_dev.units + i); /* stop unit */
break;
}
uptr = dt_dev.units + unum;
if (uptr->flags & UNIT_DIS) /* disabled? */
dt_seterr (uptr, STA_SEL); /* select err */
if ((fnc == FNC_WMRK) || /* write mark? */
((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? */ else if (((data & CSR_ERR) == 0) && /* error bit clear? */
tcst = tcst & ~STA_RWERR; ((access != WRITEB) || ((PA & 1) != 0))) { /* not write low byte? */
if (tcst & STA_ALLERR) tcst = tcst & ~STA_ALLERR; /* clear errors */
tccm = tccm | CSR_ERR; tccm = tccm & ~CSR_ERR; /* clear err flop */
} }
break; 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); /* sched done */
for (i = 0; i < DT_NUMDR; i++) /* loop thru units */
dt_stopunit (dt_dev.units + i); /* stop unit */
break;
}
uptr = dt_dev.units + unum;
if (uptr->flags & UNIT_DIS) /* disabled? */
dt_seterr (uptr, STA_SEL); /* select err */
if ((fnc == FNC_WMRK) || /* write mark? */
((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) != 0) { /* error? */
dt_stopunit (uptr); /* stop the unit */
DT_SETDONE; /* set done at once */
}
else dt_newsa (tccm); /* new function */
break;
case 002: /* TCWC */ case 002: /* TCWC */
tcwc = data; /* word write only! */ tcwc = data; /* word write only! */