| 1 | /* pdp11_tc.c: PDP-11 DECtape simulator\r |
| 2 | \r |
| 3 | Copyright (c) 1993-2006, Robert M Supnik\r |
| 4 | \r |
| 5 | Permission is hereby granted, free of charge, to any person obtaining a\r |
| 6 | copy of this software and associated documentation files (the "Software"),\r |
| 7 | to deal in the Software without restriction, including without limitation\r |
| 8 | the rights to use, copy, modify, merge, publish, distribute, sublicense,\r |
| 9 | and/or sell copies of the Software, and to permit persons to whom the\r |
| 10 | Software is furnished to do so, subject to the following conditions:\r |
| 11 | \r |
| 12 | The above copyright notice and this permission notice shall be included in\r |
| 13 | all copies or substantial portions of the Software.\r |
| 14 | \r |
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r |
| 18 | ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r |
| 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r |
| 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r |
| 21 | \r |
| 22 | Except as contained in this notice, the name of Robert M Supnik shall not be\r |
| 23 | used in advertising or otherwise to promote the sale, use or other dealings\r |
| 24 | in this Software without prior written authorization from Robert M Supnik.\r |
| 25 | \r |
| 26 | tc TC11/TU56 DECtape\r |
| 27 | \r |
| 28 | 23-Jun-06 RMS Fixed switch conflict in ATTACH\r |
| 29 | 10-Feb-06 RMS READ sets extended data bits in TCST (found by Alan Frisbie)\r |
| 30 | 16-Aug-05 RMS Fixed C++ declaration and cast problems\r |
| 31 | 07-Jul-05 RMS Removed extraneous externs\r |
| 32 | 30-Sep-04 RMS Revised Unibus interface\r |
| 33 | 25-Jan-04 RMS Revised for device debug support\r |
| 34 | 09-Jan-04 RMS Changed sim_fsize calling sequence, added STOP_OFFR\r |
| 35 | 29-Dec-03 RMS Changed initial status to disabled (in Qbus system)\r |
| 36 | 18-Oct-03 RMS Fixed reverse checksum in read all\r |
| 37 | Added DECtape off reel message\r |
| 38 | Simplified timing\r |
| 39 | 25-Apr-03 RMS Revised for extended file support\r |
| 40 | 14-Mar-03 RMS Fixed variable size interaction with save/restore\r |
| 41 | 29-Sep-02 RMS Added variable address support to bootstrap\r |
| 42 | Added vector change/display support\r |
| 43 | Added 16b format support\r |
| 44 | New data structures\r |
| 45 | 30-May-02 RMS Widened POS to 32b\r |
| 46 | 26-Jan-02 RMS Revised bootstrap to conform to M9312\r |
| 47 | 06-Jan-02 RMS Revised enable/disable support\r |
| 48 | 30-Nov-01 RMS Added read only unit, extended SET/SHOW support\r |
| 49 | 24-Nov-01 RMS Converted POS, STATT, LASTT to arrays\r |
| 50 | 09-Nov-01 RMS Added bus map support\r |
| 51 | 15-Sep-01 RMS Integrated debug logging\r |
| 52 | 27-Sep-01 RMS Fixed interrupt after stop for RSTS/E\r |
| 53 | 07-Sep-01 RMS Revised device disable and interrupt mechanisms\r |
| 54 | 29-Aug-01 RMS Added casts to PDP-8 unpack routine\r |
| 55 | 17-Jul-01 RMS Moved function prototype\r |
| 56 | 11-May-01 RMS Fixed bug in reset\r |
| 57 | 26-Apr-01 RMS Added device enable/disable support\r |
| 58 | 18-Apr-01 RMS Changed to rewind tape before boot\r |
| 59 | 16-Mar-01 RMS Fixed bug in interrupt after stop\r |
| 60 | 15-Mar-01 RMS Added 129th word to PDP-8 format\r |
| 61 | \r |
| 62 | PDP-11 DECtapes are represented in memory by fixed length buffer of 32b words.\r |
| 63 | Three file formats are supported:\r |
| 64 | \r |
| 65 | 18b/36b 256 words per block [256 x 18b]\r |
| 66 | 16b 256 words per block [256 x 16b]\r |
| 67 | 12b 129 words per block [129 x 12b]\r |
| 68 | \r |
| 69 | When a 16b or 12b DECtape file is read in, it is converted to 18b/36b format.\r |
| 70 | \r |
| 71 | DECtape motion is measured in 3b lines. Time between lines is 33.33us.\r |
| 72 | Tape density is nominally 300 lines per inch. The format of a DECtape (as\r |
| 73 | taken from the TD8E formatter) is:\r |
| 74 | \r |
| 75 | reverse end zone 8192 reverse end zone codes ~ 10 feet\r |
| 76 | reverse buffer 200 interblock codes\r |
| 77 | block 0\r |
| 78 | :\r |
| 79 | block n\r |
| 80 | forward buffer 200 interblock codes\r |
| 81 | forward end zone 8192 forward end zone codes ~ 10 feet\r |
| 82 | \r |
| 83 | A block consists of five 18b header words, a tape-specific number of data\r |
| 84 | words, and five 18b trailer words. All systems except the PDP-8 use a\r |
| 85 | standard block length of 256 words; the PDP-8 uses a standard block length\r |
| 86 | of 86 words (x 18b = 129 words x 12b).\r |
| 87 | \r |
| 88 | Because a DECtape file only contains data, the simulator cannot support\r |
| 89 | write timing and mark track and can only do a limited implementation\r |
| 90 | of read all and write all. Read all assumes that the tape has been\r |
| 91 | conventionally written forward:\r |
| 92 | \r |
| 93 | header word 0 0\r |
| 94 | header word 1 block number (for forward reads)\r |
| 95 | header words 2,3 0\r |
| 96 | header word 4 checksum (for reverse reads)\r |
| 97 | :\r |
| 98 | trailer word 4 checksum (for forward reads)\r |
| 99 | trailer words 3,2 0\r |
| 100 | trailer word 1 block number (for reverse reads)\r |
| 101 | trailer word 0 0\r |
| 102 | \r |
| 103 | Write all writes only the data words and dumps the interblock words in the\r |
| 104 | bit bucket.\r |
| 105 | */\r |
| 106 | \r |
| 107 | #include "pdp11_defs.h"\r |
| 108 | \r |
| 109 | #define DT_NUMDR 8 /* #drives */\r |
| 110 | #define DT_M_NUMDR (DT_NUMDR - 1)\r |
| 111 | #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */\r |
| 112 | #define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */\r |
| 113 | #define UNIT_V_11FMT (UNIT_V_UF + 2) /* 16b format */\r |
| 114 | #define UNIT_WLK (1 << UNIT_V_WLK)\r |
| 115 | #define UNIT_8FMT (1 << UNIT_V_8FMT)\r |
| 116 | #define UNIT_11FMT (1 << UNIT_V_11FMT)\r |
| 117 | #define STATE u3 /* unit state */\r |
| 118 | #define LASTT u4 /* last time update */\r |
| 119 | #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */\r |
| 120 | \r |
| 121 | /* System independent DECtape constants */\r |
| 122 | \r |
| 123 | #define DT_LPERMC 6 /* lines per mark track */\r |
| 124 | #define DT_BLKWD 1 /* blk no word in h/t */\r |
| 125 | #define DT_CSMWD 4 /* checksum word in h/t */\r |
| 126 | #define DT_HTWRD 5 /* header/trailer words */\r |
| 127 | #define DT_EZLIN (8192 * DT_LPERMC) /* end zone length */\r |
| 128 | #define DT_BFLIN (200 * DT_LPERMC) /* buffer length */\r |
| 129 | #define DT_BLKLN (DT_BLKWD * DT_LPERMC) /* blk no line in h/t */\r |
| 130 | #define DT_CSMLN (DT_CSMWD * DT_LPERMC) /* csum line in h/t */\r |
| 131 | #define DT_HTLIN (DT_HTWRD * DT_LPERMC) /* header/trailer lines */\r |
| 132 | \r |
| 133 | /* 16b, 18b, 36b DECtape constants */\r |
| 134 | \r |
| 135 | #define D18_WSIZE 6 /* word size in lines */\r |
| 136 | #define D18_BSIZE 256 /* block size in 18b */\r |
| 137 | #define D18_TSIZE 578 /* tape size */\r |
| 138 | #define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN)\r |
| 139 | #define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE))\r |
| 140 | #define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */\r |
| 141 | #define D16_FILSIZ (D18_TSIZE * D18_BSIZE * sizeof (int16))\r |
| 142 | \r |
| 143 | /* 12b DECtape constants */\r |
| 144 | \r |
| 145 | #define D8_WSIZE 4 /* word size in lines */\r |
| 146 | #define D8_BSIZE 86 /* block size in 18b */\r |
| 147 | #define D8_TSIZE 1474 /* tape size */\r |
| 148 | #define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN)\r |
| 149 | #define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE))\r |
| 150 | #define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */\r |
| 151 | \r |
| 152 | #define D8_NBSIZE ((D8_BSIZE * D18_WSIZE) / D8_WSIZE)\r |
| 153 | #define D8_FILSIZ (D8_NBSIZE * D8_TSIZE * sizeof (int16))\r |
| 154 | \r |
| 155 | /* This controller */\r |
| 156 | \r |
| 157 | #define DT_CAPAC D18_CAPAC /* default */\r |
| 158 | #define DT_WSIZE D18_WSIZE\r |
| 159 | \r |
| 160 | /* Calculated constants, per unit */\r |
| 161 | \r |
| 162 | #define DTU_BSIZE(u) (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE)\r |
| 163 | #define DTU_TSIZE(u) (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE)\r |
| 164 | #define DTU_LPERB(u) (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB)\r |
| 165 | #define DTU_FWDEZ(u) (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ)\r |
| 166 | #define DTU_CAPAC(u) (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC)\r |
| 167 | \r |
| 168 | #define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u))\r |
| 169 | #define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u))\r |
| 170 | #define DT_LIN2WD(p,u) ((DT_LIN2OF (p,u) - DT_HTLIN) / DT_WSIZE)\r |
| 171 | #define DT_BLK2LN(p,u) (((p) * DTU_LPERB (u)) + DT_EZLIN)\r |
| 172 | #define DT_QREZ(u) (((u)->pos) < DT_EZLIN)\r |
| 173 | #define DT_QFEZ(u) (((u)->pos) >= ((uint32) DTU_FWDEZ (u)))\r |
| 174 | #define DT_QEZ(u) (DT_QREZ (u) || DT_QFEZ (u))\r |
| 175 | \r |
| 176 | /* TCST - 177340 - status register */\r |
| 177 | \r |
| 178 | #define STA_END 0100000 /* end zone */\r |
| 179 | #define STA_PAR 0040000 /* parity err */\r |
| 180 | #define STA_MRK 0020000 /* mark trk err */\r |
| 181 | #define STA_ILO 0010000 /* illegal op */\r |
| 182 | #define STA_SEL 0004000 /* select err */\r |
| 183 | #define STA_BLKM 0002000 /* block miss err */\r |
| 184 | #define STA_DATM 0001000 /* data miss err */\r |
| 185 | #define STA_NXM 0000400 /* nx mem err */\r |
| 186 | #define STA_UPS 0000200 /* up to speed */\r |
| 187 | #define STA_V_XD 0 /* extended data */\r |
| 188 | #define STA_M_XD 03\r |
| 189 | #define STA_ALLERR (STA_END | STA_PAR | STA_MRK | STA_ILO | \\r |
| 190 | STA_SEL | STA_BLKM | STA_DATM | STA_NXM )\r |
| 191 | #define STA_RWERR (STA_END | STA_PAR | STA_MRK | \\r |
| 192 | STA_BLKM | STA_DATM | STA_NXM )\r |
| 193 | #define STA_RW 0000003\r |
| 194 | #define STA_GETXD(x) (((x) >> STA_V_XD) & STA_M_XD)\r |
| 195 | \r |
| 196 | /* TCCM - 177342 - command register */\r |
| 197 | \r |
| 198 | /* #define CSR_ERR 0100000 */\r |
| 199 | #define CSR_MNT 0020000 /* maint (unimpl) */\r |
| 200 | #define CSR_INH 0010000 /* delay inhibit */\r |
| 201 | #define CSR_DIR 0004000 /* reverse */\r |
| 202 | #define CSR_V_UNIT 8 /* unit select */\r |
| 203 | #define CSR_M_UNIT 07\r |
| 204 | #define CSR_UNIT (CSR_M_UNIT << CSR_V_UNIT)\r |
| 205 | /* #define CSR_DONE 0000200 */\r |
| 206 | /* #define CSR_IE 0000100 */\r |
| 207 | #define CSR_V_MEX 4 /* mem extension */\r |
| 208 | #define CSR_M_MEX 03\r |
| 209 | #define CSR_MEX (CSR_M_MEX << CSR_V_MEX)\r |
| 210 | #define CSR_V_FNC 1 /* function */\r |
| 211 | #define CSR_M_FNC 07\r |
| 212 | #define FNC_STOP 00 /* stop all */\r |
| 213 | #define FNC_SRCH 01 /* search */\r |
| 214 | #define FNC_READ 02 /* read */\r |
| 215 | #define FNC_RALL 03 /* read all */\r |
| 216 | #define FNC_SSEL 04 /* stop selected */\r |
| 217 | #define FNC_WMRK 05 /* write */\r |
| 218 | #define FNC_WRIT 06 /* write all */\r |
| 219 | #define FNC_WALL 07 /* write timing */\r |
| 220 | /* define CSR_GO 0000001 */\r |
| 221 | #define CSR_RW 0117576 /* read/write */\r |
| 222 | \r |
| 223 | #define CSR_GETUNIT(x) (((x) >> CSR_V_UNIT) & CSR_M_UNIT)\r |
| 224 | #define CSR_GETMEX(x) (((x) >> CSR_V_MEX) & CSR_M_MEX)\r |
| 225 | #define CSR_GETFNC(x) (((x) >> CSR_V_FNC) & CSR_M_FNC)\r |
| 226 | #define CSR_INCMEX(x) (((x) & ~CSR_MEX) | (((x) + (1 << CSR_V_MEX)) & CSR_MEX))\r |
| 227 | \r |
| 228 | /* TCWC - 177344 - word count */\r |
| 229 | \r |
| 230 | /* TCBA - 177346 - bus address */\r |
| 231 | \r |
| 232 | /* TCDT - 177350 - data */\r |
| 233 | \r |
| 234 | /* DECtape state */\r |
| 235 | \r |
| 236 | #define DTS_V_MOT 3 /* motion */\r |
| 237 | #define DTS_M_MOT 07\r |
| 238 | #define DTS_STOP 0 /* stopped */\r |
| 239 | #define DTS_DECF 2 /* decel, fwd */\r |
| 240 | #define DTS_DECR 3 /* decel, rev */\r |
| 241 | #define DTS_ACCF 4 /* accel, fwd */\r |
| 242 | #define DTS_ACCR 5 /* accel, rev */\r |
| 243 | #define DTS_ATSF 6 /* @speed, fwd */\r |
| 244 | #define DTS_ATSR 7 /* @speed, rev */\r |
| 245 | #define DTS_DIR 01 /* dir mask */\r |
| 246 | #define DTS_V_FNC 0 /* function */\r |
| 247 | #define DTS_M_FNC 07\r |
| 248 | #define DTS_OFR FNC_WMRK /* "off reel" */\r |
| 249 | #define DTS_GETMOT(x) (((x) >> DTS_V_MOT) & DTS_M_MOT)\r |
| 250 | #define DTS_GETFNC(x) (((x) >> DTS_V_FNC) & DTS_M_FNC)\r |
| 251 | #define DTS_V_2ND 6 /* next state */\r |
| 252 | #define DTS_V_3RD (DTS_V_2ND + DTS_V_2ND) /* next next */\r |
| 253 | #define DTS_STA(y,z) (((y) << DTS_V_MOT) | ((z) << DTS_V_FNC))\r |
| 254 | #define DTS_SETSTA(y,z) uptr->STATE = DTS_STA (y, z)\r |
| 255 | #define DTS_SET2ND(y,z) uptr->STATE = (uptr->STATE & 077) | \\r |
| 256 | ((DTS_STA (y, z)) << DTS_V_2ND)\r |
| 257 | #define DTS_SET3RD(y,z) uptr->STATE = (uptr->STATE & 07777) | \\r |
| 258 | ((DTS_STA (y, z)) << DTS_V_3RD)\r |
| 259 | #define DTS_NXTSTA(x) (x >> DTS_V_2ND)\r |
| 260 | \r |
| 261 | /* Logging */\r |
| 262 | \r |
| 263 | #define LOG_MS 0x1\r |
| 264 | #define LOG_RW 0x2\r |
| 265 | #define LOG_BL 0x4\r |
| 266 | \r |
| 267 | #define DT_SETDONE tccm = tccm | CSR_DONE; \\r |
| 268 | if (tccm & CSR_IE) SET_INT (DTA)\r |
| 269 | #define DT_CLRDONE tccm = tccm & ~CSR_DONE; \\r |
| 270 | CLR_INT (DTA)\r |
| 271 | #define ABS(x) (((x) < 0)? (-(x)): (x))\r |
| 272 | \r |
| 273 | extern uint16 *M; /* memory */\r |
| 274 | extern int32 int_req[IPL_HLVL];\r |
| 275 | extern UNIT cpu_unit;\r |
| 276 | extern int32 sim_switches;\r |
| 277 | extern FILE *sim_deb;\r |
| 278 | \r |
| 279 | int32 tcst = 0; /* status */\r |
| 280 | int32 tccm = 0; /* command */\r |
| 281 | int32 tcwc = 0; /* word count */\r |
| 282 | int32 tcba = 0; /* bus address */\r |
| 283 | int32 tcdt = 0; /* data */\r |
| 284 | int32 dt_ctime = 100; /* fast cmd time */\r |
| 285 | int32 dt_ltime = 12; /* interline time */\r |
| 286 | int32 dt_dctime = 40000; /* decel time */\r |
| 287 | int32 dt_substate = 0;\r |
| 288 | int32 dt_logblk = 0;\r |
| 289 | int32 dt_stopoffr = 0;\r |
| 290 | \r |
| 291 | DEVICE dt_dev;\r |
| 292 | t_stat dt_rd (int32 *data, int32 PA, int32 access);\r |
| 293 | t_stat dt_wr (int32 data, int32 PA, int32 access);\r |
| 294 | t_stat dt_svc (UNIT *uptr);\r |
| 295 | t_stat dt_svcdone (UNIT *uptr);\r |
| 296 | t_stat dt_reset (DEVICE *dptr);\r |
| 297 | t_stat dt_attach (UNIT *uptr, char *cptr);\r |
| 298 | t_stat dt_detach (UNIT *uptr);\r |
| 299 | t_stat dt_boot (int32 unitno, DEVICE *dptr);\r |
| 300 | void dt_deselect (int32 oldf);\r |
| 301 | void dt_newsa (int32 newf);\r |
| 302 | void dt_newfnc (UNIT *uptr, int32 newsta);\r |
| 303 | t_bool dt_setpos (UNIT *uptr);\r |
| 304 | void dt_schedez (UNIT *uptr, int32 dir);\r |
| 305 | void dt_seterr (UNIT *uptr, int32 e);\r |
| 306 | void dt_stopunit (UNIT *uptr);\r |
| 307 | int32 dt_comobv (int32 val);\r |
| 308 | int32 dt_csum (UNIT *uptr, int32 blk);\r |
| 309 | int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos);\r |
| 310 | extern int32 sim_is_running;\r |
| 311 | \r |
| 312 | /* DT data structures\r |
| 313 | \r |
| 314 | dt_dev DT device descriptor\r |
| 315 | dt_unit DT unit list\r |
| 316 | dt_reg DT register list\r |
| 317 | dt_mod DT modifier list\r |
| 318 | */\r |
| 319 | \r |
| 320 | DIB dt_dib = {\r |
| 321 | IOBA_TC, IOLN_TC, &dt_rd, &dt_wr,\r |
| 322 | 1, IVCL (DTA), VEC_DTA, { NULL }\r |
| 323 | };\r |
| 324 | \r |
| 325 | UNIT dt_unit[] = {\r |
| 326 | { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 327 | UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) },\r |
| 328 | { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 329 | UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) },\r |
| 330 | { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 331 | UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) },\r |
| 332 | { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 333 | UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) },\r |
| 334 | { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 335 | UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) },\r |
| 336 | { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 337 | UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) },\r |
| 338 | { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 339 | UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) },\r |
| 340 | { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 341 | UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) },\r |
| 342 | { UDATA (&dt_svcdone, UNIT_DIS, 0) }\r |
| 343 | };\r |
| 344 | \r |
| 345 | #define DT_TIMER (DT_NUMDR)\r |
| 346 | \r |
| 347 | REG dt_reg[] = {\r |
| 348 | { ORDATA (TCST, tcst, 16) },\r |
| 349 | { ORDATA (TCCM, tccm, 16) },\r |
| 350 | { ORDATA (TCWC, tcwc, 16) },\r |
| 351 | { ORDATA (TCBA, tcba, 16) },\r |
| 352 | { ORDATA (TCDT, tcdt, 16) },\r |
| 353 | { FLDATA (INT, IREQ (DTA), INT_V_DTA) },\r |
| 354 | { FLDATA (ERR, tccm, CSR_V_ERR) },\r |
| 355 | { FLDATA (DONE, tccm, CSR_V_DONE) },\r |
| 356 | { FLDATA (IE, tccm, CSR_V_DONE) },\r |
| 357 | { DRDATA (CTIME, dt_ctime, 31), REG_NZ },\r |
| 358 | { DRDATA (LTIME, dt_ltime, 31), REG_NZ },\r |
| 359 | { DRDATA (DCTIME, dt_dctime, 31), REG_NZ },\r |
| 360 | { ORDATA (SUBSTATE, dt_substate, 1) },\r |
| 361 | { DRDATA (LBLK, dt_logblk, 12), REG_HIDDEN },\r |
| 362 | { URDATA (POS, dt_unit[0].pos, 10, T_ADDR_W, 0,\r |
| 363 | DT_NUMDR, PV_LEFT | REG_RO) },\r |
| 364 | { URDATA (STATT, dt_unit[0].STATE, 8, 18, 0,\r |
| 365 | DT_NUMDR, REG_RO) },\r |
| 366 | { URDATA (LASTT, dt_unit[0].LASTT, 10, 32, 0,\r |
| 367 | DT_NUMDR, REG_HRO) },\r |
| 368 | { FLDATA (STOP_OFFR, dt_stopoffr, 0) },\r |
| 369 | { ORDATA (DEVADDR, dt_dib.ba, 32), REG_HRO },\r |
| 370 | { ORDATA (DEVVEC, dt_dib.vec, 16), REG_HRO },\r |
| 371 | { NULL }\r |
| 372 | };\r |
| 373 | \r |
| 374 | MTAB dt_mod[] = {\r |
| 375 | { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },\r |
| 376 | { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, \r |
| 377 | { UNIT_8FMT + UNIT_11FMT, 0, "18b", NULL, NULL },\r |
| 378 | { UNIT_8FMT + UNIT_11FMT, UNIT_8FMT, "12b", NULL, NULL },\r |
| 379 | { UNIT_8FMT + UNIT_11FMT, UNIT_11FMT, "16b", NULL, NULL },\r |
| 380 | { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS",\r |
| 381 | &set_addr, &show_addr, NULL },\r |
| 382 | { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR",\r |
| 383 | &set_vec, &show_vec, NULL },\r |
| 384 | { 0 }\r |
| 385 | };\r |
| 386 | \r |
| 387 | DEBTAB dt_deb[] = {\r |
| 388 | { "MOTION", LOG_MS },\r |
| 389 | { "DATA", LOG_RW },\r |
| 390 | { "BLOCK", LOG_BL },\r |
| 391 | { NULL, 0 }\r |
| 392 | };\r |
| 393 | \r |
| 394 | DEVICE dt_dev = {\r |
| 395 | "TC", dt_unit, dt_reg, dt_mod,\r |
| 396 | DT_NUMDR + 1, 8, 24, 1, 8, 18,\r |
| 397 | NULL, NULL, &dt_reset,\r |
| 398 | &dt_boot, &dt_attach, &dt_detach,\r |
| 399 | &dt_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG, 0,\r |
| 400 | dt_deb, NULL, NULL\r |
| 401 | };\r |
| 402 | \r |
| 403 | /* IO dispatch routines, I/O addresses 17777340 - 17777350 */\r |
| 404 | \r |
| 405 | t_stat dt_rd (int32 *data, int32 PA, int32 access)\r |
| 406 | {\r |
| 407 | int32 j, unum, mot, fnc;\r |
| 408 | \r |
| 409 | j = (PA >> 1) & 017; /* get reg offset */\r |
| 410 | unum = CSR_GETUNIT (tccm); /* get drive */\r |
| 411 | switch (j) {\r |
| 412 | \r |
| 413 | case 000: /* TCST */\r |
| 414 | mot = DTS_GETMOT (dt_unit[unum].STATE); /* get motion */\r |
| 415 | if (mot >= DTS_ATSF) tcst = tcst | STA_UPS; /* set/clr speed */\r |
| 416 | else tcst = tcst & ~STA_UPS;\r |
| 417 | *data = tcst;\r |
| 418 | break;\r |
| 419 | \r |
| 420 | case 001: /* TCCM */\r |
| 421 | if (tcst & STA_ALLERR) tccm = tccm | CSR_ERR; /* set/clr error */\r |
| 422 | else tccm = tccm & ~CSR_ERR;\r |
| 423 | *data = tccm;\r |
| 424 | break;\r |
| 425 | \r |
| 426 | case 002: /* TCWC */\r |
| 427 | *data = tcwc;\r |
| 428 | break;\r |
| 429 | \r |
| 430 | case 003: /* TCBA */\r |
| 431 | *data = tcba;\r |
| 432 | break;\r |
| 433 | \r |
| 434 | case 004: /* TCDT */\r |
| 435 | fnc = DTS_GETFNC (dt_unit[unum].STATE); /* get function */\r |
| 436 | if (fnc == FNC_RALL) { /* read all? */\r |
| 437 | DT_CLRDONE; /* clear done */\r |
| 438 | }\r |
| 439 | *data = tcdt;\r |
| 440 | break;\r |
| 441 | }\r |
| 442 | \r |
| 443 | return SCPE_OK;\r |
| 444 | }\r |
| 445 | \r |
| 446 | t_stat dt_wr (int32 data, int32 PA, int32 access)\r |
| 447 | {\r |
| 448 | int32 i, j, unum, old_tccm, fnc;\r |
| 449 | UNIT *uptr;\r |
| 450 | \r |
| 451 | j = (PA >> 1) & 017; /* get reg offset */\r |
| 452 | switch (j) {\r |
| 453 | \r |
| 454 | case 000: /* TCST */\r |
| 455 | if ((access == WRITEB) && (PA & 1)) break;\r |
| 456 | tcst = (tcst & ~STA_RW) | (data & STA_RW);\r |
| 457 | break;\r |
| 458 | \r |
| 459 | case 001: /* TCCM */\r |
| 460 | old_tccm = tccm; /* save prior */\r |
| 461 | if (access == WRITEB) data = (PA & 1)?\r |
| 462 | (tccm & 0377) | (data << 8): (tccm & ~0377) | data;\r |
| 463 | if ((data & CSR_IE) == 0) CLR_INT (DTA);\r |
| 464 | else if ((((tccm & CSR_IE) == 0) && (tccm & CSR_DONE)) ||\r |
| 465 | (data & CSR_DONE)) SET_INT (DTA);\r |
| 466 | tccm = (tccm & ~CSR_RW) | (data & CSR_RW);\r |
| 467 | if ((data & CSR_GO) && (tccm & CSR_DONE)) { /* new cmd? */\r |
| 468 | tcst = tcst & ~STA_ALLERR; /* clear errors */\r |
| 469 | tccm = tccm & ~(CSR_ERR | CSR_DONE); /* clear done, err */\r |
| 470 | CLR_INT (DTA); /* clear int */\r |
| 471 | if ((old_tccm ^ tccm) & CSR_UNIT) dt_deselect (old_tccm);\r |
| 472 | unum = CSR_GETUNIT (tccm); /* get drive */\r |
| 473 | fnc = CSR_GETFNC (tccm); /* get function */\r |
| 474 | if (fnc == FNC_STOP) { /* stop all? */\r |
| 475 | sim_activate (&dt_dev.units[DT_TIMER], dt_ctime);\r |
| 476 | for (i = 0; i < DT_NUMDR; i++)\r |
| 477 | dt_stopunit (dt_dev.units + i); /* stop unit */\r |
| 478 | break;\r |
| 479 | }\r |
| 480 | uptr = dt_dev.units + unum;\r |
| 481 | if (uptr->flags & UNIT_DIS) /* disabled? */\r |
| 482 | dt_seterr (uptr, STA_SEL); /* select err */\r |
| 483 | if ((fnc == FNC_WMRK) || /* write mark? */\r |
| 484 | ((fnc == FNC_WALL) && (uptr->flags & UNIT_WPRT)) ||\r |
| 485 | ((fnc == FNC_WRIT) && (uptr->flags & UNIT_WPRT)))\r |
| 486 | dt_seterr (uptr, STA_ILO); /* illegal op */\r |
| 487 | if (!(tccm & CSR_ERR)) dt_newsa (tccm);\r |
| 488 | }\r |
| 489 | else if ((tccm & CSR_ERR) == 0) { /* clear err? */\r |
| 490 | tcst = tcst & ~STA_RWERR;\r |
| 491 | if (tcst & STA_ALLERR) tccm = tccm | CSR_ERR;\r |
| 492 | }\r |
| 493 | break;\r |
| 494 | \r |
| 495 | case 002: /* TCWC */\r |
| 496 | tcwc = data; /* word write only! */\r |
| 497 | break;\r |
| 498 | \r |
| 499 | case 003: /* TCBA */\r |
| 500 | tcba = data; /* word write only! */\r |
| 501 | break; \r |
| 502 | \r |
| 503 | case 004: /* TCDT */\r |
| 504 | unum = CSR_GETUNIT (tccm); /* get drive */\r |
| 505 | fnc = DTS_GETFNC (dt_unit[unum].STATE); /* get function */\r |
| 506 | if (fnc == FNC_WALL) { /* write all? */\r |
| 507 | DT_CLRDONE; /* clear done */\r |
| 508 | }\r |
| 509 | tcdt = data; /* word write only! */\r |
| 510 | break;\r |
| 511 | }\r |
| 512 | \r |
| 513 | return SCPE_OK;\r |
| 514 | }\r |
| 515 | \r |
| 516 | /* Unit deselect */\r |
| 517 | \r |
| 518 | void dt_deselect (int32 oldf)\r |
| 519 | {\r |
| 520 | int32 old_unit = CSR_GETUNIT (oldf);\r |
| 521 | UNIT *uptr = dt_dev.units + old_unit;\r |
| 522 | int32 old_mot = DTS_GETMOT (uptr->STATE);\r |
| 523 | \r |
| 524 | if (old_mot >= DTS_ATSF) /* at speed? */\r |
| 525 | dt_newfnc (uptr, DTS_STA (old_mot, DTS_OFR));\r |
| 526 | else if (old_mot >= DTS_ACCF) /* accelerating? */\r |
| 527 | DTS_SET2ND (DTS_ATSF | (old_mot & DTS_DIR), DTS_OFR);\r |
| 528 | return;\r |
| 529 | }\r |
| 530 | \r |
| 531 | /* New operation\r |
| 532 | \r |
| 533 | 1. If function = stop\r |
| 534 | - if not already stopped or decelerating, schedule deceleration\r |
| 535 | - schedule command completion\r |
| 536 | 2. If change in direction,\r |
| 537 | - if not decelerating, schedule deceleration\r |
| 538 | - set accelerating (other dir) as next state\r |
| 539 | - set function as next next state\r |
| 540 | 3. If not accelerating or at speed,\r |
| 541 | - schedule acceleration\r |
| 542 | - set function as next state\r |
| 543 | 4. If not yet at speed,\r |
| 544 | - set function as next state\r |
| 545 | 5. If at speed,\r |
| 546 | - set function as current state, schedule function\r |
| 547 | */\r |
| 548 | \r |
| 549 | void dt_newsa (int32 newf)\r |
| 550 | {\r |
| 551 | int32 new_unit, prev_mot, new_fnc;\r |
| 552 | int32 prev_dir, new_dir;\r |
| 553 | UNIT *uptr;\r |
| 554 | \r |
| 555 | new_unit = CSR_GETUNIT (newf); /* new, old units */\r |
| 556 | uptr = dt_dev.units + new_unit;\r |
| 557 | if ((uptr->flags & UNIT_ATT) == 0) { /* new unit attached? */\r |
| 558 | dt_seterr (uptr, STA_SEL); /* no, error */\r |
| 559 | return;\r |
| 560 | }\r |
| 561 | prev_mot = DTS_GETMOT (uptr->STATE); /* previous motion */\r |
| 562 | prev_dir = prev_mot & DTS_DIR; /* previous dir */\r |
| 563 | new_fnc = CSR_GETFNC (newf); /* new function */\r |
| 564 | new_dir = (newf & CSR_DIR) != 0; /* new di? */\r |
| 565 | \r |
| 566 | if (new_fnc == FNC_SSEL) { /* stop unit? */\r |
| 567 | sim_activate (&dt_dev.units[DT_TIMER], dt_ctime); /* sched done */\r |
| 568 | dt_stopunit (uptr); /* stop unit */\r |
| 569 | return;\r |
| 570 | }\r |
| 571 | \r |
| 572 | if (prev_mot == DTS_STOP) { /* start? */\r |
| 573 | if (dt_setpos (uptr)) return; /* update pos */\r |
| 574 | sim_cancel (uptr); /* stop current */\r |
| 575 | sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* sched accel */\r |
| 576 | DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */\r |
| 577 | DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */\r |
| 578 | return;\r |
| 579 | }\r |
| 580 | \r |
| 581 | if (prev_dir ^ new_dir) { /* dir chg? */\r |
| 582 | dt_stopunit (uptr); /* stop unit */\r |
| 583 | DTS_SET2ND (DTS_ACCF | new_dir, 0); /* next = accel */\r |
| 584 | DTS_SET3RD (DTS_ATSF | new_dir, new_fnc); /* next next = fnc */\r |
| 585 | return;\r |
| 586 | }\r |
| 587 | \r |
| 588 | if (prev_mot < DTS_ACCF) { /* not accel/at speed? */\r |
| 589 | if (dt_setpos (uptr)) return; /* update pos */\r |
| 590 | sim_cancel (uptr); /* cancel cur */\r |
| 591 | sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* sched accel */\r |
| 592 | DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */\r |
| 593 | DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */\r |
| 594 | return;\r |
| 595 | }\r |
| 596 | \r |
| 597 | if (prev_mot < DTS_ATSF) { /* not at speed? */\r |
| 598 | DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */\r |
| 599 | return;\r |
| 600 | }\r |
| 601 | \r |
| 602 | dt_newfnc (uptr, DTS_STA (DTS_ATSF | new_dir, new_fnc));/* state = fnc */\r |
| 603 | return; \r |
| 604 | }\r |
| 605 | \r |
| 606 | /* Schedule new DECtape function\r |
| 607 | \r |
| 608 | This routine is only called if\r |
| 609 | - the selected unit is attached\r |
| 610 | - the selected unit is at speed (forward or backward)\r |
| 611 | \r |
| 612 | This routine\r |
| 613 | - updates the selected unit's position\r |
| 614 | - updates the selected unit's state\r |
| 615 | - schedules the new operation\r |
| 616 | */\r |
| 617 | \r |
| 618 | void dt_newfnc (UNIT *uptr, int32 newsta)\r |
| 619 | {\r |
| 620 | int32 fnc, dir, blk, unum, relpos, newpos;\r |
| 621 | uint32 oldpos;\r |
| 622 | \r |
| 623 | oldpos = uptr->pos; /* save old pos */\r |
| 624 | if (dt_setpos (uptr)) return; /* update pos */\r |
| 625 | uptr->STATE = newsta; /* update state */\r |
| 626 | fnc = DTS_GETFNC (uptr->STATE); /* set variables */\r |
| 627 | dir = DTS_GETMOT (uptr->STATE) & DTS_DIR;\r |
| 628 | unum = (int32) (uptr - dt_dev.units);\r |
| 629 | if (oldpos == uptr->pos)\r |
| 630 | uptr->pos = uptr->pos + (dir? -1: 1);\r |
| 631 | blk = DT_LIN2BL (uptr->pos, uptr);\r |
| 632 | \r |
| 633 | if (dir? DT_QREZ (uptr): DT_QFEZ (uptr)) { /* wrong ez? */\r |
| 634 | dt_seterr (uptr, STA_END); /* set ez flag, stop */\r |
| 635 | return;\r |
| 636 | }\r |
| 637 | dt_substate = 0; /* substate = normal */\r |
| 638 | sim_cancel (uptr); /* cancel cur op */\r |
| 639 | switch (fnc) { /* case function */\r |
| 640 | \r |
| 641 | case DTS_OFR: /* off reel */\r |
| 642 | if (dir) newpos = -1000; /* rev? < start */\r |
| 643 | else newpos = DTU_FWDEZ (uptr) + DT_EZLIN + 1000; /* fwd? > end */\r |
| 644 | break;\r |
| 645 | \r |
| 646 | case FNC_SRCH: /* search */\r |
| 647 | if (dir) newpos = DT_BLK2LN ((DT_QFEZ (uptr)?\r |
| 648 | DTU_TSIZE (uptr): blk), uptr) - DT_BLKLN - DT_WSIZE;\r |
| 649 | else newpos = DT_BLK2LN ((DT_QREZ (uptr)?\r |
| 650 | 0: blk + 1), uptr) + DT_BLKLN + (DT_WSIZE - 1);\r |
| 651 | if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: searching %s\n",\r |
| 652 | unum, (dir? "backward": "forward"));\r |
| 653 | break;\r |
| 654 | \r |
| 655 | case FNC_WRIT: /* write */\r |
| 656 | case FNC_READ: /* read */\r |
| 657 | if (DT_QEZ (uptr)) { /* in "ok" end zone? */\r |
| 658 | if (dir) newpos = DTU_FWDEZ (uptr) - DT_HTLIN - DT_WSIZE;\r |
| 659 | else newpos = DT_EZLIN + DT_HTLIN + (DT_WSIZE - 1);\r |
| 660 | break;\r |
| 661 | }\r |
| 662 | relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */\r |
| 663 | if ((relpos >= DT_HTLIN) && /* in data zone? */\r |
| 664 | (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) {\r |
| 665 | dt_seterr (uptr, STA_BLKM);\r |
| 666 | return;\r |
| 667 | }\r |
| 668 | if (dir) newpos = DT_BLK2LN (((relpos >= (DTU_LPERB (uptr) - DT_HTLIN))?\r |
| 669 | blk + 1: blk), uptr) - DT_HTLIN - DT_WSIZE;\r |
| 670 | else newpos = DT_BLK2LN (((relpos < DT_HTLIN)?\r |
| 671 | blk: blk + 1), uptr) + DT_HTLIN + (DT_WSIZE - 1);\r |
| 672 | if (DEBUG_PRI (dt_dev, LOG_RW) ||\r |
| 673 | (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk)))\r |
| 674 | fprintf (sim_deb, ">>DT%d: %s block %d %s\n",\r |
| 675 | unum, ((fnc == FNC_READ)? "read": "write"),\r |
| 676 | blk, (dir? "backward": "forward"));\r |
| 677 | break;\r |
| 678 | \r |
| 679 | case FNC_RALL: /* read all */\r |
| 680 | case FNC_WALL: /* write all */\r |
| 681 | if (DT_QEZ (uptr)) { /* in "ok" end zone? */\r |
| 682 | if (dir) newpos = DTU_FWDEZ (uptr) - DT_WSIZE;\r |
| 683 | else newpos = DT_EZLIN + (DT_WSIZE - 1);\r |
| 684 | }\r |
| 685 | else {\r |
| 686 | relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */\r |
| 687 | if (dir? (relpos < (DTU_LPERB (uptr) - DT_CSMLN)): /* switch in time? */\r |
| 688 | (relpos >= DT_CSMLN)) {\r |
| 689 | dt_seterr (uptr, STA_BLKM);\r |
| 690 | return;\r |
| 691 | }\r |
| 692 | if (dir) newpos = DT_BLK2LN (blk + 1, uptr) - DT_CSMLN - DT_WSIZE;\r |
| 693 | else newpos = DT_BLK2LN (blk, uptr) + DT_CSMLN + (DT_WSIZE - 1);\r |
| 694 | }\r |
| 695 | if (fnc == FNC_WALL) sim_activate /* write all? */\r |
| 696 | (&dt_dev.units[DT_TIMER], dt_ctime); /* sched done */\r |
| 697 | if (DEBUG_PRI (dt_dev, LOG_RW) ||\r |
| 698 | (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk)))\r |
| 699 | fprintf (sim_deb, ">>DT%d: read all block %d %s\n",\r |
| 700 | unum, blk, (dir? "backward": "forward"));\r |
| 701 | break;\r |
| 702 | \r |
| 703 | default:\r |
| 704 | dt_seterr (uptr, STA_SEL); /* bad state */\r |
| 705 | return;\r |
| 706 | }\r |
| 707 | \r |
| 708 | sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime);\r |
| 709 | return;\r |
| 710 | }\r |
| 711 | \r |
| 712 | /* Update DECtape position\r |
| 713 | \r |
| 714 | DECtape motion is modeled as a constant velocity, with linear\r |
| 715 | acceleration and deceleration. The motion equations are as follows:\r |
| 716 | \r |
| 717 | t = time since operation started\r |
| 718 | tmax = time for operation (accel, decel only)\r |
| 719 | v = at speed velocity in lines (= 1/dt_ltime)\r |
| 720 | \r |
| 721 | Then:\r |
| 722 | at speed dist = t * v\r |
| 723 | accel dist = (t^2 * v) / (2 * tmax)\r |
| 724 | decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax)\r |
| 725 | \r |
| 726 | This routine uses the relative (integer) time, rather than the absolute\r |
| 727 | (floating point) time, to allow save and restore of the start times.\r |
| 728 | */\r |
| 729 | \r |
| 730 | t_bool dt_setpos (UNIT *uptr)\r |
| 731 | {\r |
| 732 | uint32 new_time, ut, ulin, udelt;\r |
| 733 | int32 mot = DTS_GETMOT (uptr->STATE);\r |
| 734 | int32 unum, delta;\r |
| 735 | \r |
| 736 | new_time = sim_grtime (); /* current time */\r |
| 737 | ut = new_time - uptr->LASTT; /* elapsed time */\r |
| 738 | if (ut == 0) return FALSE; /* no time gone? exit */\r |
| 739 | uptr->LASTT = new_time; /* update last time */\r |
| 740 | switch (mot & ~DTS_DIR) { /* case on motion */\r |
| 741 | \r |
| 742 | case DTS_STOP: /* stop */\r |
| 743 | delta = 0;\r |
| 744 | break;\r |
| 745 | \r |
| 746 | case DTS_DECF: /* slowing */\r |
| 747 | ulin = ut / (uint32) dt_ltime;\r |
| 748 | udelt = dt_dctime / dt_ltime;\r |
| 749 | delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt);\r |
| 750 | break;\r |
| 751 | \r |
| 752 | case DTS_ACCF: /* accelerating */\r |
| 753 | ulin = ut / (uint32) dt_ltime;\r |
| 754 | udelt = (dt_dctime - (dt_dctime >> 2)) / dt_ltime;\r |
| 755 | delta = (ulin * ulin) / (2 * udelt);\r |
| 756 | break;\r |
| 757 | \r |
| 758 | case DTS_ATSF: /* at speed */\r |
| 759 | delta = ut / (uint32) dt_ltime;\r |
| 760 | break;\r |
| 761 | }\r |
| 762 | \r |
| 763 | if (mot & DTS_DIR) uptr->pos = uptr->pos - delta; /* update pos */\r |
| 764 | else uptr->pos = uptr->pos + delta;\r |
| 765 | if (((int32) uptr->pos < 0) ||\r |
| 766 | ((int32) uptr->pos > (DTU_FWDEZ (uptr) + DT_EZLIN))) {\r |
| 767 | detach_unit (uptr); /* off reel? */\r |
| 768 | uptr->STATE = uptr->pos = 0;\r |
| 769 | unum = (int32) (uptr - dt_dev.units);\r |
| 770 | if ((unum == CSR_GETUNIT (tccm)) && (CSR_GETFNC (tccm) != FNC_STOP))\r |
| 771 | dt_seterr (uptr, STA_SEL); /* error */\r |
| 772 | return TRUE;\r |
| 773 | }\r |
| 774 | return FALSE;\r |
| 775 | }\r |
| 776 | \r |
| 777 | /* Command timer service after stop - set done */\r |
| 778 | \r |
| 779 | t_stat dt_svcdone (UNIT *uptr)\r |
| 780 | {\r |
| 781 | DT_SETDONE;\r |
| 782 | return SCPE_OK;\r |
| 783 | }\r |
| 784 | \r |
| 785 | /* Unit service\r |
| 786 | \r |
| 787 | Unit must be attached, detach cancels operation\r |
| 788 | */\r |
| 789 | \r |
| 790 | t_stat dt_svc (UNIT *uptr)\r |
| 791 | {\r |
| 792 | int32 mot = DTS_GETMOT (uptr->STATE);\r |
| 793 | int32 dir = mot & DTS_DIR;\r |
| 794 | int32 fnc = DTS_GETFNC (uptr->STATE);\r |
| 795 | int32 *fbuf = (int32 *) uptr->filebuf;\r |
| 796 | int32 blk, wrd, relpos, dat;\r |
| 797 | uint32 ba, ma;\r |
| 798 | uint16 wbuf;\r |
| 799 | \r |
| 800 | /* Motion cases\r |
| 801 | \r |
| 802 | Decelerating - if next state != stopped, must be accel reverse\r |
| 803 | Accelerating - next state must be @speed, schedule function\r |
| 804 | At speed - do functional processing\r |
| 805 | */\r |
| 806 | \r |
| 807 | switch (mot) {\r |
| 808 | \r |
| 809 | case DTS_DECF: case DTS_DECR: /* decelerating */\r |
| 810 | if (dt_setpos (uptr)) /* upd pos; off reel? */\r |
| 811 | return IORETURN (dt_stopoffr, STOP_DTOFF);\r |
| 812 | uptr->STATE = DTS_NXTSTA (uptr->STATE); /* advance state */\r |
| 813 | if (uptr->STATE) /* not stopped? */\r |
| 814 | sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* reversing */\r |
| 815 | return SCPE_OK;\r |
| 816 | \r |
| 817 | case DTS_ACCF: case DTS_ACCR: /* accelerating */\r |
| 818 | dt_newfnc (uptr, DTS_NXTSTA (uptr->STATE)); /* adv state, sched */\r |
| 819 | return SCPE_OK;\r |
| 820 | \r |
| 821 | case DTS_ATSF: case DTS_ATSR: /* at speed */\r |
| 822 | break; /* check function */\r |
| 823 | \r |
| 824 | default: /* other */\r |
| 825 | dt_seterr (uptr, STA_SEL); /* state error */\r |
| 826 | return SCPE_OK;\r |
| 827 | }\r |
| 828 | \r |
| 829 | /* Functional cases\r |
| 830 | \r |
| 831 | Search - transfer block number, schedule next block\r |
| 832 | Off reel - detach unit (it must be deselected)\r |
| 833 | */\r |
| 834 | \r |
| 835 | if (dt_setpos (uptr)) /* upd pos; off reel? */\r |
| 836 | return IORETURN (dt_stopoffr, STOP_DTOFF);\r |
| 837 | if (DT_QEZ (uptr)) { /* in end zone? */\r |
| 838 | dt_seterr (uptr, STA_END); /* end zone error */\r |
| 839 | return SCPE_OK;\r |
| 840 | }\r |
| 841 | blk = DT_LIN2BL (uptr->pos, uptr); /* get block # */\r |
| 842 | \r |
| 843 | switch (fnc) { /* at speed, check fnc */\r |
| 844 | \r |
| 845 | case FNC_SRCH: /* search */\r |
| 846 | tcdt = blk; /* set block # */\r |
| 847 | dt_schedez (uptr, dir); /* sched end zone */\r |
| 848 | DT_SETDONE; /* set done */\r |
| 849 | break;\r |
| 850 | \r |
| 851 | case DTS_OFR: /* off reel */\r |
| 852 | detach_unit (uptr); /* must be deselected */\r |
| 853 | uptr->STATE = uptr->pos = 0; /* no visible action */\r |
| 854 | break;\r |
| 855 | \r |
| 856 | /* Read\r |
| 857 | \r |
| 858 | If wc ovf has not occurred, inc ma, wc and copy word from tape to memory\r |
| 859 | If wc ovf, set flag\r |
| 860 | If not end of block, schedule next word\r |
| 861 | If end of block and not wc ovf, schedule next block\r |
| 862 | If end of block and wc ovf, set done, schedule end zone\r |
| 863 | */\r |
| 864 | \r |
| 865 | case FNC_READ: /* read */\r |
| 866 | wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */\r |
| 867 | if (!dt_substate) { /* !wc ovf? */\r |
| 868 | ma = (CSR_GETMEX (tccm) << 16) | tcba; /* form 18b addr */\r |
| 869 | ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */\r |
| 870 | tcdt = wbuf = fbuf[ba] & DMASK; /* read word */\r |
| 871 | tcst = (tcst & ~STA_M_XD) | ((fbuf[ma] >> 16) & STA_M_XD);\r |
| 872 | if (Map_WriteW (ma, 2, &wbuf)) { /* store, nxm? */\r |
| 873 | dt_seterr (uptr, STA_NXM);\r |
| 874 | break;\r |
| 875 | }\r |
| 876 | tcwc = (tcwc + 1) & DMASK; /* incr MA, WC */\r |
| 877 | tcba = (tcba + 2) & DMASK;\r |
| 878 | if (tcba <= 1) tccm = CSR_INCMEX (tccm);\r |
| 879 | if (tcwc == 0) dt_substate = 1;\r |
| 880 | }\r |
| 881 | if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not end blk? */\r |
| 882 | sim_activate (uptr, DT_WSIZE * dt_ltime);\r |
| 883 | else if (dt_substate) { /* wc ovf? */\r |
| 884 | dt_schedez (uptr, dir); /* sched end zone */\r |
| 885 | DT_SETDONE; /* set done */\r |
| 886 | }\r |
| 887 | else sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime);\r |
| 888 | break; \r |
| 889 | \r |
| 890 | /* Write\r |
| 891 | \r |
| 892 | If wc ovf has not occurred, inc ma, wc\r |
| 893 | Copy word from memory (or 0, to fill block) to tape\r |
| 894 | If wc ovf, set flag\r |
| 895 | If not end of block, schedule next word\r |
| 896 | If end of block and not wc ovf, schedule next block\r |
| 897 | If end of block and wc ovf, set done, schedule end zone\r |
| 898 | */\r |
| 899 | \r |
| 900 | case FNC_WRIT: /* write */\r |
| 901 | wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */\r |
| 902 | if (dt_substate) tcdt = 0; /* wc ovf? fill */\r |
| 903 | else {\r |
| 904 | ma = (CSR_GETMEX (tccm) << 16) | tcba; /* form 18b addr */\r |
| 905 | if (Map_ReadW (ma, 2, &wbuf)) { /* fetch word */\r |
| 906 | dt_seterr (uptr, STA_NXM);\r |
| 907 | break;\r |
| 908 | }\r |
| 909 | tcdt = wbuf; /* get word */\r |
| 910 | tcwc = (tcwc + 1) & DMASK; /* incr MA, WC */\r |
| 911 | tcba = (tcba + 2) & DMASK;\r |
| 912 | if (tcba <= 1) tccm = CSR_INCMEX (tccm);\r |
| 913 | }\r |
| 914 | ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */\r |
| 915 | fbuf[ba] = tcdt; /* write word */\r |
| 916 | if (ba >= uptr->hwmark) uptr->hwmark = ba + 1;\r |
| 917 | if (tcwc == 0) dt_substate = 1;\r |
| 918 | if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not end blk? */\r |
| 919 | sim_activate (uptr, DT_WSIZE * dt_ltime);\r |
| 920 | else if (dt_substate) { /* wc ovf? */\r |
| 921 | dt_schedez (uptr, dir); /* sched end zone */\r |
| 922 | DT_SETDONE;\r |
| 923 | }\r |
| 924 | else sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime);\r |
| 925 | break; \r |
| 926 | \r |
| 927 | /* Read all - read current header or data word */\r |
| 928 | \r |
| 929 | case FNC_RALL:\r |
| 930 | if (tccm & CSR_DONE) { /* done set? */\r |
| 931 | dt_seterr (uptr, STA_DATM); /* data miss */\r |
| 932 | break;\r |
| 933 | }\r |
| 934 | relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */\r |
| 935 | if ((relpos >= DT_HTLIN) && /* in data zone? */\r |
| 936 | (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) {\r |
| 937 | wrd = DT_LIN2WD (uptr->pos, uptr);\r |
| 938 | ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */\r |
| 939 | dat = fbuf[ba]; /* get tape word */\r |
| 940 | }\r |
| 941 | else dat = dt_gethdr (uptr, blk, relpos); /* get hdr */\r |
| 942 | if (dir) dat = dt_comobv (dat); /* rev? comp obv */\r |
| 943 | tcdt = dat & DMASK; /* low 16b */\r |
| 944 | tcst = (tcst & ~STA_M_XD) | ((dat >> 16) & STA_M_XD);\r |
| 945 | sim_activate (uptr, DT_WSIZE * dt_ltime);\r |
| 946 | DT_SETDONE; /* set done */\r |
| 947 | break;\r |
| 948 | \r |
| 949 | /* Write all - write current header or data word */\r |
| 950 | \r |
| 951 | case FNC_WALL:\r |
| 952 | if (tccm & CSR_DONE) { /* done set? */\r |
| 953 | dt_seterr (uptr, STA_DATM); /* data miss */\r |
| 954 | break;\r |
| 955 | }\r |
| 956 | relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */\r |
| 957 | if ((relpos >= DT_HTLIN) && /* in data zone? */\r |
| 958 | (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) {\r |
| 959 | wrd = DT_LIN2WD (uptr->pos, uptr);\r |
| 960 | dat = (STA_GETXD (tcst) << 16) | tcdt; /* get data word */\r |
| 961 | if (dir) dat = dt_comobv (dat); /* rev? comp obv */\r |
| 962 | ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */\r |
| 963 | fbuf[ba] = dat; /* write word */\r |
| 964 | if (ba >= uptr->hwmark) uptr->hwmark = ba + 1;\r |
| 965 | }\r |
| 966 | /* else /* ignore hdr */ \r |
| 967 | sim_activate (uptr, DT_WSIZE * dt_ltime);\r |
| 968 | DT_SETDONE; /* set done */\r |
| 969 | break;\r |
| 970 | \r |
| 971 | default:\r |
| 972 | dt_seterr (uptr, STA_SEL); /* impossible state */\r |
| 973 | break;\r |
| 974 | }\r |
| 975 | return SCPE_OK;\r |
| 976 | }\r |
| 977 | \r |
| 978 | /* Utility routines */\r |
| 979 | \r |
| 980 | /* Set error flag */\r |
| 981 | \r |
| 982 | void dt_seterr (UNIT *uptr, int32 e)\r |
| 983 | {\r |
| 984 | int32 mot = DTS_GETMOT (uptr->STATE);\r |
| 985 | \r |
| 986 | tcst = tcst | e; /* set error flag */\r |
| 987 | tccm = tccm | CSR_ERR;\r |
| 988 | if (!(tccm & CSR_DONE)) { /* not done? */\r |
| 989 | DT_SETDONE;\r |
| 990 | }\r |
| 991 | if (mot >= DTS_ACCF) { /* ~stopped or stopping? */\r |
| 992 | sim_cancel (uptr); /* cancel activity */\r |
| 993 | if (dt_setpos (uptr)) return; /* update position */\r |
| 994 | sim_activate (uptr, dt_dctime); /* sched decel */\r |
| 995 | DTS_SETSTA (DTS_DECF | (mot & DTS_DIR), 0); /* state = decel */\r |
| 996 | }\r |
| 997 | return;\r |
| 998 | }\r |
| 999 | \r |
| 1000 | /* Stop unit */\r |
| 1001 | \r |
| 1002 | void dt_stopunit (UNIT *uptr)\r |
| 1003 | {\r |
| 1004 | int32 mot = DTS_GETMOT (uptr->STATE);\r |
| 1005 | int32 dir = mot & DTS_DIR;\r |
| 1006 | \r |
| 1007 | if (mot == DTS_STOP) return; /* already stopped? */\r |
| 1008 | if ((mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */\r |
| 1009 | if (dt_setpos (uptr)) return; /* update pos */\r |
| 1010 | sim_cancel (uptr); /* stop current */\r |
| 1011 | sim_activate (uptr, dt_dctime); /* schedule decel */\r |
| 1012 | }\r |
| 1013 | DTS_SETSTA (DTS_DECF | dir, 0); /* state = decel */\r |
| 1014 | return;\r |
| 1015 | }\r |
| 1016 | \r |
| 1017 | /* Schedule end zone */\r |
| 1018 | \r |
| 1019 | void dt_schedez (UNIT *uptr, int32 dir)\r |
| 1020 | {\r |
| 1021 | int32 newpos;\r |
| 1022 | \r |
| 1023 | if (dir) newpos = DT_EZLIN - DT_WSIZE; /* rev? rev ez */\r |
| 1024 | else newpos = DTU_FWDEZ (uptr) + DT_WSIZE; /* fwd? fwd ez */\r |
| 1025 | sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime);\r |
| 1026 | return;\r |
| 1027 | }\r |
| 1028 | \r |
| 1029 | /* Complement obverse routine (18b) */\r |
| 1030 | \r |
| 1031 | int32 dt_comobv (int32 dat)\r |
| 1032 | {\r |
| 1033 | dat = dat ^ 0777777; /* compl obverse */\r |
| 1034 | dat = ((dat >> 15) & 07) | ((dat >> 9) & 070) |\r |
| 1035 | ((dat >> 3) & 0700) | ((dat & 0700) << 3) |\r |
| 1036 | ((dat & 070) << 9) | ((dat & 07) << 15);\r |
| 1037 | return dat;\r |
| 1038 | }\r |
| 1039 | \r |
| 1040 | /* Checksum routine */\r |
| 1041 | \r |
| 1042 | int32 dt_csum (UNIT *uptr, int32 blk)\r |
| 1043 | {\r |
| 1044 | int32 *fbuf = (int32 *) uptr->filebuf;\r |
| 1045 | int32 ba = blk * DTU_BSIZE (uptr);\r |
| 1046 | int32 i, csum, wrd;\r |
| 1047 | \r |
| 1048 | csum = 077; /* init csum */\r |
| 1049 | for (i = 0; i < DTU_BSIZE (uptr); i++) { /* loop thru buf */\r |
| 1050 | wrd = fbuf[ba + i] ^ 0777777; /* get ~word */\r |
| 1051 | csum = csum ^ (wrd >> 12) ^ (wrd >> 6) ^ wrd;\r |
| 1052 | }\r |
| 1053 | return (csum & 077);\r |
| 1054 | }\r |
| 1055 | \r |
| 1056 | /* Get header word (18b) */\r |
| 1057 | \r |
| 1058 | int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos)\r |
| 1059 | {\r |
| 1060 | int32 wrd = relpos / DT_WSIZE;\r |
| 1061 | \r |
| 1062 | if (wrd == DT_BLKWD) return blk; /* fwd blknum */\r |
| 1063 | if (wrd == DT_CSMWD) return 077; /* rev csum */\r |
| 1064 | if (wrd == (2 * DT_HTWRD + DTU_BSIZE (uptr) - DT_CSMWD - 1)) /* fwd csum */\r |
| 1065 | return (dt_csum (uptr, blk) << 12);\r |
| 1066 | if (wrd == (2 * DT_HTWRD + DTU_BSIZE (uptr) - DT_BLKWD - 1)) /* rev blkno */\r |
| 1067 | return dt_comobv (blk);\r |
| 1068 | return 0; /* all others */\r |
| 1069 | }\r |
| 1070 | \r |
| 1071 | /* Reset routine */\r |
| 1072 | \r |
| 1073 | t_stat dt_reset (DEVICE *dptr)\r |
| 1074 | {\r |
| 1075 | int32 i, prev_mot;\r |
| 1076 | UNIT *uptr;\r |
| 1077 | \r |
| 1078 | for (i = 0; i < DT_NUMDR; i++) { /* stop all activity */\r |
| 1079 | uptr = dt_dev.units + i;\r |
| 1080 | if (sim_is_running) { /* RESET? */\r |
| 1081 | prev_mot = DTS_GETMOT (uptr->STATE); /* get motion */\r |
| 1082 | if ((prev_mot & ~DTS_DIR) > DTS_DECF) { /* accel or spd? */\r |
| 1083 | if (dt_setpos (uptr)) continue; /* update pos */\r |
| 1084 | sim_cancel (uptr);\r |
| 1085 | sim_activate (uptr, dt_dctime); /* sched decel */\r |
| 1086 | DTS_SETSTA (DTS_DECF | (prev_mot & DTS_DIR), 0);\r |
| 1087 | }\r |
| 1088 | }\r |
| 1089 | else {\r |
| 1090 | sim_cancel (uptr); /* sim reset */\r |
| 1091 | uptr->STATE = 0; \r |
| 1092 | uptr->LASTT = sim_grtime ();\r |
| 1093 | }\r |
| 1094 | }\r |
| 1095 | tcst = tcwc = tcba = tcdt = 0; /* clear reg */\r |
| 1096 | tccm = CSR_DONE;\r |
| 1097 | CLR_INT (DTA); /* clear int req */\r |
| 1098 | return SCPE_OK;\r |
| 1099 | }\r |
| 1100 | \r |
| 1101 | /* Device bootstrap */\r |
| 1102 | \r |
| 1103 | #define BOOT_START 02000 /* start */\r |
| 1104 | #define BOOT_ENTRY (BOOT_START + 002) /* entry */\r |
| 1105 | #define BOOT_UNIT (BOOT_START + 010) /* unit number */\r |
| 1106 | #define BOOT_CSR (BOOT_START + 020) /* CSR */\r |
| 1107 | #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16))\r |
| 1108 | \r |
| 1109 | static const uint16 boot_rom[] = {\r |
| 1110 | 0042124, /* "TD" */\r |
| 1111 | 0012706, BOOT_START, /* MOV #boot_start, SP */\r |
| 1112 | 0012700, 0000000, /* MOV #unit, R0 ; unit number */\r |
| 1113 | 0010003, /* MOV R0, R3 */\r |
| 1114 | 0000303, /* SWAB R3 */\r |
| 1115 | 0012701, 0177342, /* MOV #TCCM, R1 ; csr */\r |
| 1116 | 0012702, 0004003, /* RW: MOV #4003, R2 ; rev+rnum+go */\r |
| 1117 | 0050302, /* BIS R3, R2 */\r |
| 1118 | 0010211, /* MOV R2, (R1) ; load csr */\r |
| 1119 | 0032711, 0100200, /* BIT #100200, (R1) ; wait */\r |
| 1120 | 0001775, /* BEQ .-4 */\r |
| 1121 | 0100370, /* BPL RW ; no err, cont */\r |
| 1122 | 0005761, 0177776, /* TST -2(R1) ; end zone? */\r |
| 1123 | 0100036, /* BPL ER ; no, err */\r |
| 1124 | 0012702, 0000003, /* MOV #3, R2 ; rnum+go */\r |
| 1125 | 0050302, /* BIS R3, R2 */\r |
| 1126 | 0010211, /* MOV R2, (R1) ; load csr */\r |
| 1127 | 0032711, 0100200, /* BIT #100200, (R1) ; wait */\r |
| 1128 | 0001775, /* BEQ .-4 */\r |
| 1129 | 0100426, /* BMI ER ; err, die */\r |
| 1130 | 0005761, 0000006, /* TST 6(R1) ; blk 0? */\r |
| 1131 | 0001023, /* BNE ER ; no, die */\r |
| 1132 | 0012761, 0177000, 0000002, /* MOV #-256.*2, 2(R1) ; load wc */\r |
| 1133 | 0005061, 0000004, /* CLR 4(R1) ; clear ba */\r |
| 1134 | 0012702, 0000005, /* MOV #READ+GO, R2 ; read & go */\r |
| 1135 | 0050302, /* BIS R3, R2 */\r |
| 1136 | 0010211, /* MOV R2, (R1) ; load csr */\r |
| 1137 | 0005002, /* CLR R2 */\r |
| 1138 | 0005003, /* CLR R3 */\r |
| 1139 | 0012704, BOOT_START+020, /* MOV #START+20, R4 */\r |
| 1140 | 0005005, /* CLR R5 */\r |
| 1141 | 0032711, 0100200, /* BIT #100200, (R1) ; wait */\r |
| 1142 | 0001775, /* BEQ .-4 */\r |
| 1143 | 0100401, /* BMI ER ; err, die */\r |
| 1144 | 0005007, /* CLR PC */\r |
| 1145 | 0012711, 0000001, /* ER: MOV #1, (R1) ; stop all */\r |
| 1146 | 0000000 /* HALT */\r |
| 1147 | };\r |
| 1148 | \r |
| 1149 | t_stat dt_boot (int32 unitno, DEVICE *dptr)\r |
| 1150 | {\r |
| 1151 | int32 i;\r |
| 1152 | extern int32 saved_PC;\r |
| 1153 | \r |
| 1154 | dt_unit[unitno].pos = DT_EZLIN;\r |
| 1155 | for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i];\r |
| 1156 | M[BOOT_UNIT >> 1] = unitno & DT_M_NUMDR;\r |
| 1157 | M[BOOT_CSR >> 1] = (dt_dib.ba & DMASK) + 02;\r |
| 1158 | saved_PC = BOOT_ENTRY;\r |
| 1159 | return SCPE_OK;\r |
| 1160 | }\r |
| 1161 | \r |
| 1162 | /* Attach routine\r |
| 1163 | \r |
| 1164 | Determine 12b, 16b, or 18b/36b format\r |
| 1165 | Allocate buffer\r |
| 1166 | If 12b, read 12b format and convert to 18b in buffer\r |
| 1167 | If 16b, read 16b format and convert to 18b in buffer\r |
| 1168 | If 18b/36b, read data into buffer\r |
| 1169 | */\r |
| 1170 | \r |
| 1171 | t_stat dt_attach (UNIT *uptr, char *cptr)\r |
| 1172 | {\r |
| 1173 | uint16 pdp8b[D8_NBSIZE];\r |
| 1174 | uint16 pdp11b[D18_BSIZE];\r |
| 1175 | uint32 ba, sz, k, *fbuf;\r |
| 1176 | int32 u = uptr - dt_dev.units;\r |
| 1177 | t_stat r;\r |
| 1178 | \r |
| 1179 | r = attach_unit (uptr, cptr); /* attach */\r |
| 1180 | if (r != SCPE_OK) return r; /* fail? */\r |
| 1181 | if ((sim_switches & SIM_SW_REST) == 0) { /* not from rest? */\r |
| 1182 | uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT; /* default 16b */\r |
| 1183 | if (sim_switches & SWMASK ('T')) /* att 12b? */\r |
| 1184 | uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT;\r |
| 1185 | else if (sim_switches & SWMASK ('F')) /* att 18b? */\r |
| 1186 | uptr->flags = uptr->flags & ~(UNIT_8FMT | UNIT_11FMT);\r |
| 1187 | else if (!(sim_switches & SWMASK ('A')) && /* autosize? */\r |
| 1188 | ((sz = sim_fsize (uptr->fileref)) > D16_FILSIZ)) {\r |
| 1189 | if (sz <= D8_FILSIZ)\r |
| 1190 | uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT;\r |
| 1191 | else uptr->flags = uptr->flags & ~(UNIT_8FMT | UNIT_11FMT);\r |
| 1192 | }\r |
| 1193 | }\r |
| 1194 | uptr->capac = DTU_CAPAC (uptr); /* set capacity */\r |
| 1195 | uptr->filebuf = calloc (uptr->capac, sizeof (uint32));\r |
| 1196 | if (uptr->filebuf == NULL) { /* can't alloc? */\r |
| 1197 | detach_unit (uptr);\r |
| 1198 | return SCPE_MEM;\r |
| 1199 | }\r |
| 1200 | fbuf = (uint32 *) uptr->filebuf; /* file buffer */\r |
| 1201 | printf ("%s%d: ", sim_dname (&dt_dev), u);\r |
| 1202 | if (uptr->flags & UNIT_8FMT) printf ("12b format");\r |
| 1203 | else if (uptr->flags & UNIT_11FMT) printf ("16b format");\r |
| 1204 | else printf ("18b/36b format");\r |
| 1205 | printf (", buffering file in memory\n");\r |
| 1206 | if (uptr->flags & UNIT_8FMT) { /* 12b? */\r |
| 1207 | for (ba = 0; ba < uptr->capac; ) { /* loop thru file */\r |
| 1208 | k = fxread (pdp8b, sizeof (int16), D8_NBSIZE, uptr->fileref);\r |
| 1209 | if (k == 0) break;\r |
| 1210 | for ( ; k < D8_NBSIZE; k++) pdp8b[k] = 0;\r |
| 1211 | for (k = 0; k < D8_NBSIZE; k = k + 3) { /* loop thru blk */\r |
| 1212 | fbuf[ba] = ((uint32) (pdp8b[k] & 07777) << 6) |\r |
| 1213 | ((uint32) (pdp8b[k + 1] >> 6) & 077);\r |
| 1214 | fbuf[ba + 1] = ((uint32) (pdp8b[k + 1] & 077) << 12) |\r |
| 1215 | ((uint32) pdp8b[k + 2] & 07777);\r |
| 1216 | ba = ba + 2;\r |
| 1217 | } /* end blk loop */\r |
| 1218 | } /* end file loop */\r |
| 1219 | uptr->hwmark = ba;\r |
| 1220 | } /* end if */\r |
| 1221 | else if (uptr->flags & UNIT_11FMT) { /* 16b? */\r |
| 1222 | for (ba = 0; ba < uptr->capac; ) { /* loop thru file */\r |
| 1223 | k = fxread (pdp11b, sizeof (uint16), D18_BSIZE, uptr->fileref);\r |
| 1224 | if (k == 0) break;\r |
| 1225 | for ( ; k < D18_BSIZE; k++) pdp11b[k] = 0;\r |
| 1226 | for (k = 0; k < D18_BSIZE; k++)\r |
| 1227 | fbuf[ba++] = pdp11b[k];\r |
| 1228 | }\r |
| 1229 | uptr->hwmark = ba;\r |
| 1230 | } /* end elif */\r |
| 1231 | else uptr->hwmark = fxread (uptr->filebuf, sizeof (uint32),\r |
| 1232 | uptr->capac, uptr->fileref);\r |
| 1233 | uptr->flags = uptr->flags | UNIT_BUF; /* set buf flag */\r |
| 1234 | uptr->pos = DT_EZLIN; /* beyond leader */\r |
| 1235 | uptr->LASTT = sim_grtime (); /* last pos update */\r |
| 1236 | return SCPE_OK;\r |
| 1237 | }\r |
| 1238 | \r |
| 1239 | /* Detach routine\r |
| 1240 | \r |
| 1241 | Cancel in progress operation\r |
| 1242 | If 12b, convert 18b buffer to 12b and write to file\r |
| 1243 | If 16b, convert 18b buffer to 16b and write to file\r |
| 1244 | If 18b/36b, write buffer to file\r |
| 1245 | Deallocate buffer\r |
| 1246 | */\r |
| 1247 | \r |
| 1248 | t_stat dt_detach (UNIT* uptr)\r |
| 1249 | {\r |
| 1250 | uint16 pdp8b[D8_NBSIZE];\r |
| 1251 | uint16 pdp11b[D18_BSIZE];\r |
| 1252 | uint32 ba, k, *fbuf;\r |
| 1253 | int32 u = uptr - dt_dev.units;\r |
| 1254 | \r |
| 1255 | if (!(uptr->flags & UNIT_ATT)) return SCPE_OK;\r |
| 1256 | if (sim_is_active (uptr)) { /* active? cancel op */\r |
| 1257 | sim_cancel (uptr);\r |
| 1258 | if ((u == CSR_GETUNIT (tccm)) && ((tccm & CSR_DONE) == 0)) {\r |
| 1259 | tcst = tcst | STA_SEL;\r |
| 1260 | tccm = tccm | CSR_ERR | CSR_DONE;\r |
| 1261 | if (tccm & CSR_IE) SET_INT (DTA);\r |
| 1262 | }\r |
| 1263 | uptr->STATE = uptr->pos = 0;\r |
| 1264 | }\r |
| 1265 | fbuf = (uint32 *) uptr->filebuf; /* file buffer */\r |
| 1266 | if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) { /* any data? */\r |
| 1267 | printf ("%s%d: writing buffer to file\n", sim_dname (&dt_dev), u);\r |
| 1268 | rewind (uptr->fileref); /* start of file */\r |
| 1269 | if (uptr->flags & UNIT_8FMT) { /* 12b? */\r |
| 1270 | for (ba = 0; ba < uptr->hwmark; ) { /* loop thru file */\r |
| 1271 | for (k = 0; k < D8_NBSIZE; k = k + 3) { /* loop blk */\r |
| 1272 | pdp8b[k] = (fbuf[ba] >> 6) & 07777;\r |
| 1273 | pdp8b[k + 1] = ((fbuf[ba] & 077) << 6) |\r |
| 1274 | ((fbuf[ba + 1] >> 12) & 077);\r |
| 1275 | pdp8b[k + 2] = fbuf[ba + 1] & 07777;\r |
| 1276 | ba = ba + 2;\r |
| 1277 | } /* end loop blk */\r |
| 1278 | fxwrite (pdp8b, sizeof (uint16), D8_NBSIZE, uptr->fileref);\r |
| 1279 | if (ferror (uptr->fileref)) break;\r |
| 1280 | } /* end loop file */\r |
| 1281 | } /* end if 12b */\r |
| 1282 | else if (uptr->flags & UNIT_11FMT) { /* 16b? */\r |
| 1283 | for (ba = 0; ba < uptr->hwmark; ) { /* loop thru file */\r |
| 1284 | for (k = 0; k < D18_BSIZE; k++) /* loop blk */\r |
| 1285 | pdp11b[k] = fbuf[ba++] & DMASK;\r |
| 1286 | fxwrite (pdp11b, sizeof (uint16), D18_BSIZE, uptr->fileref);\r |
| 1287 | if (ferror (uptr->fileref)) break;\r |
| 1288 | } /* end loop file */\r |
| 1289 | } /* end if 16b */\r |
| 1290 | else fxwrite (uptr->filebuf, sizeof (uint32), /* write file */\r |
| 1291 | uptr->hwmark, uptr->fileref);\r |
| 1292 | if (ferror (uptr->fileref)) perror ("I/O error");\r |
| 1293 | } /* end if hwmark */\r |
| 1294 | free (uptr->filebuf); /* release buf */\r |
| 1295 | uptr->flags = uptr->flags & ~UNIT_BUF; /* clear buf flag */\r |
| 1296 | uptr->filebuf = NULL; /* clear buf ptr */\r |
| 1297 | uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT; /* default fmt */\r |
| 1298 | uptr->capac = DT_CAPAC; /* default size */\r |
| 1299 | return detach_unit (uptr);\r |
| 1300 | }\r |