diff --git a/PDP11/pdp11_tc.c b/PDP11/pdp11_tc.c index d424c9d6..fe9a2f5e 100644 --- a/PDP11/pdp11_tc.c +++ b/PDP11/pdp11_tc.c @@ -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; @@ -472,43 +502,45 @@ 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? */ + if (access == WRITEB) + 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); - 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); + tccm = tccm & ~(CSR_ERR|CSR_DONE); /* clear done, err flops */ } - else if ((tccm & CSR_ERR) == 0) { /* clear err? */ - tcst = tcst & ~STA_RWERR; - if (tcst & STA_ALLERR) - tccm = tccm | CSR_ERR; + 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 */ } - 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 */ tcwc = data; /* word write only! */