| 1 | /* h316_dp.c: Honeywell 4623, 4651, 4720 disk simulator\r |
| 2 | \r |
| 3 | Copyright (c) 2003-2005, 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 | dp 4623 disk subsystem\r |
| 27 | 4651 disk subsystem\r |
| 28 | 4720 disk subsystem\r |
| 29 | \r |
| 30 | 04-Sep-05 RMS Fixed missing return (found by Peter Schorn)\r |
| 31 | 15-Jul-05 RMS Fixed bug in attach routine\r |
| 32 | 01-Dec-04 RMS Fixed bug in skip on !seeking\r |
| 33 | \r |
| 34 | The Honeywell disks have the unique characteristic of supporting variable\r |
| 35 | formatting, on a per track basis. To accomodate this, each track is\r |
| 36 | simulated as 2048 words, divided into records. (2048 words accomodates\r |
| 37 | the largest record of 1891 + 8 overhead words.) A record is structured\r |
| 38 | as follows:\r |
| 39 | \r |
| 40 | word 0 record length n (0 = end of track)\r |
| 41 | word 1 record address (16b, uninterpreted by the simulator)\r |
| 42 | word 2 record extension (0 to 4 words of permitted 'overwrite')\r |
| 43 | word 3 first data word\r |
| 44 | :\r |
| 45 | word 3+n-1 last data word\r |
| 46 | word 3+n checksum word\r |
| 47 | word 4+n first extension word\r |
| 48 | :\r |
| 49 | word 7+n fourth extension word\r |
| 50 | word 8+n start of next record\r |
| 51 | \r |
| 52 | Formatting is done in two ways. The SET DPn FORMAT=k command formats\r |
| 53 | unit n with k records per track, each record having the maximum allowable\r |
| 54 | record size and a standard record address; or with k words per record.\r |
| 55 | Alternately, the simulator allows programmatic formating. When a track\r |
| 56 | is formated, the program supplies record parameters as follows:\r |
| 57 | \r |
| 58 | word 0 record address\r |
| 59 | words 1-n data words\r |
| 60 | word n+1 gap size in bits\r |
| 61 | \r |
| 62 | To make this work, the simulator tracks the consumption of bits in the\r |
| 63 | track, against the track capacity in bits. Bit consumption is:\r |
| 64 | \r |
| 65 | 16.5 * 16 for overhead (including address and checksum)\r |
| 66 | n * 16 for data\r |
| 67 | 'gap' for gap, which must be at least 5% of the record length\r |
| 68 | */\r |
| 69 | \r |
| 70 | #include "h316_defs.h"\r |
| 71 | #include <math.h>\r |
| 72 | \r |
| 73 | #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */\r |
| 74 | #define UNIT_WLK (1 << UNIT_V_WLK)\r |
| 75 | #define FNC u3 /* saved function */\r |
| 76 | #define CYL u4 /* actual cylinder */\r |
| 77 | #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write prot */\r |
| 78 | #define DP_TRKLEN 2048 /* track length, words */\r |
| 79 | #define DP_NUMDRV 8 /* max # drives */\r |
| 80 | #define DP_NUMTYP 3 /* # controller types */\r |
| 81 | \r |
| 82 | /* Record format */\r |
| 83 | \r |
| 84 | #define REC_LNT 0 /* length (unextended) */\r |
| 85 | #define REC_ADDR 1 /* address */\r |
| 86 | #define REC_EXT 2 /* extension (0-4) */\r |
| 87 | #define REC_DATA 3 /* start of data */\r |
| 88 | #define REC_OVHD 8 /* overhead words */\r |
| 89 | #define REC_MAXEXT 4 /* maximum extension */\r |
| 90 | #define REC_OVHD_WRDS 16.5 /* 16.5 words */\r |
| 91 | #define REC_OVHD_BITS ((16 * 16) + 8)\r |
| 92 | \r |
| 93 | /* Status word, ^ = dynamic */\r |
| 94 | \r |
| 95 | #define STA_BUSY 0100000 /* busy */\r |
| 96 | #define STA_RDY 0040000 /* ready */\r |
| 97 | #define STA_ADRER 0020000 /* address error */\r |
| 98 | #define STA_FMTER 0010000 /* format error */\r |
| 99 | #define STA_HNLER 0004000 /* heads not loaded (NI) */\r |
| 100 | #define STA_OFLER 0002000 /* offline */\r |
| 101 | #define STA_SEKER 0001000 /* seek error */\r |
| 102 | #define STA_MBZ 0000700\r |
| 103 | #define STA_WPRER 0000040 /* write prot error */\r |
| 104 | #define STA_UNSER 0000020 /* unsafe */\r |
| 105 | #define STA_CSMER 0000010 /* checksum error */\r |
| 106 | #define STA_DTRER 0000004 /* transfer rate error */\r |
| 107 | #define STA_ANYER 0000002 /* any error^ */\r |
| 108 | #define STA_EOR 0000001 /* end of record */\r |
| 109 | #define STA_ALLERR (STA_ADRER|STA_FMTER|STA_HNLER|STA_OFLER|STA_SEKER|\\r |
| 110 | STA_WPRER|STA_UNSER|STA_DTRER)\r |
| 111 | \r |
| 112 | /* Functions */\r |
| 113 | \r |
| 114 | #define FNC_SK0 0000 /* recalibrate */\r |
| 115 | #define FNC_SEEK 0001 /* seek */\r |
| 116 | #define FNC_RCA 0002 /* read current */\r |
| 117 | #define FNC_UNL 0004 /* unload */\r |
| 118 | #define FNC_FMT 0005 /* format */\r |
| 119 | #define FNC_RW 0006 /* read/write */\r |
| 120 | #define FNC_STOP 0010 /* stop format */\r |
| 121 | #define FNC_RDS 0011 /* read status */\r |
| 122 | #define FNC_DMA 0013 /* DMA/DMC */\r |
| 123 | #define FNC_AKI 0014 /* acknowledge intr */\r |
| 124 | #define FNC_IOBUS 0017 /* IO bus */\r |
| 125 | #define FNC_2ND 0020 /* second state */\r |
| 126 | #define FNC_3RD 0040 /* third state */\r |
| 127 | #define FNC_4TH 0060 /* fourth state */\r |
| 128 | #define FNC_5TH 0100 /* fifth state */\r |
| 129 | \r |
| 130 | /* Command word 1 */\r |
| 131 | \r |
| 132 | #define CW1_RW 0100000 /* read/write */\r |
| 133 | #define CW1_DIR 0000400 /* seek direction */\r |
| 134 | #define CW1_V_UNIT 11 /* unit select */\r |
| 135 | #define CW1_V_HEAD 6 /* head select */\r |
| 136 | #define CW1_V_OFFS 0 /* seek offset */\r |
| 137 | #define CW1_GETUNIT(x) (((x) >> CW1_V_UNIT) & dp_tab[dp_ctype].umsk)\r |
| 138 | #define CW1_GETHEAD(x) (((x) >> CW1_V_HEAD) & dp_tab[dp_ctype].hmsk)\r |
| 139 | #define CW1_GETOFFS(x) (((x) >> CW1_V_OFFS) & dp_tab[dp_ctype].cmsk)\r |
| 140 | \r |
| 141 | /* OTA states */\r |
| 142 | \r |
| 143 | #define OTA_NOP 0 /* normal */\r |
| 144 | #define OTA_CW1 1 /* expecting CW1 */\r |
| 145 | #define OTA_CW2 2 /* expecting CW2 */\r |
| 146 | \r |
| 147 | /* Transfer state */\r |
| 148 | \r |
| 149 | #define XIP_UMSK 007 /* unit mask */\r |
| 150 | #define XIP_SCHED 010 /* scheduled */\r |
| 151 | #define XIP_WRT 020 /* write */\r |
| 152 | #define XIP_FMT 040 /* format */\r |
| 153 | \r |
| 154 | /* The H316/516 disk emulator supports three disk controllers:\r |
| 155 | \r |
| 156 | controller units cylinders surfaces data words per track\r |
| 157 | \r |
| 158 | 4651 4 203 2 1908.25\r |
| 159 | 4623 8 203 10 1816.5\r |
| 160 | 4720 8 203 20 1908.25\r |
| 161 | \r |
| 162 | Disk types may not be intermixed on the same controller.\r |
| 163 | */\r |
| 164 | \r |
| 165 | #define TYPE_4651 0\r |
| 166 | #define UNIT_4651 4\r |
| 167 | #define CYL_4651 203\r |
| 168 | #define SURF_4651 2\r |
| 169 | #define WRDS_4651 1908.25\r |
| 170 | #define UMSK_4651 0003\r |
| 171 | #define HMSK_4651 0001\r |
| 172 | #define CMSK_4651 0377\r |
| 173 | #define CAP_4651 (CYL_4651*SURF_4651*DP_TRKLEN)\r |
| 174 | \r |
| 175 | #define TYPE_4623 1\r |
| 176 | #define UNIT_4623 8\r |
| 177 | #define CYL_4623 203\r |
| 178 | #define SURF_4623 10\r |
| 179 | #define WRDS_4623 1816.5\r |
| 180 | #define UMSK_4623 0007\r |
| 181 | #define HMSK_4623 0017\r |
| 182 | #define CMSK_4623 0377\r |
| 183 | #define CAP_4623 (CYL_4623*SURF_4623*DP_TRKLEN)\r |
| 184 | \r |
| 185 | #define TYPE_4720 2\r |
| 186 | #define UNIT_4720 8\r |
| 187 | #define CYL_4720 203\r |
| 188 | #define SURF_4720 20\r |
| 189 | #define WRDS_4720 1908.25\r |
| 190 | #define UMSK_4720 0007\r |
| 191 | #define HMSK_4720 0037\r |
| 192 | #define CMSK_4720 0377\r |
| 193 | #define CAP_4720 (CYL_4720*SURF_4720*DP_TRKLEN)\r |
| 194 | \r |
| 195 | struct drvtyp {\r |
| 196 | char *name;\r |
| 197 | uint32 numu;\r |
| 198 | uint32 cyl;\r |
| 199 | uint32 surf;\r |
| 200 | uint32 cap;\r |
| 201 | uint32 umsk;\r |
| 202 | uint32 hmsk;\r |
| 203 | uint32 cmsk;\r |
| 204 | float wrds;\r |
| 205 | };\r |
| 206 | \r |
| 207 | #define DP_DRV(d) \\r |
| 208 | #d, \\r |
| 209 | UNIT_##d, CYL_##d, SURF_##d, CAP_##d, \\r |
| 210 | UMSK_##d, HMSK_##d, CMSK_##d, WRDS_##d\r |
| 211 | \r |
| 212 | static struct drvtyp dp_tab[] = {\r |
| 213 | { DP_DRV (4651) },\r |
| 214 | { DP_DRV (4623) },\r |
| 215 | { DP_DRV (4720) }\r |
| 216 | };\r |
| 217 | \r |
| 218 | extern int32 dev_int, dev_enb, chan_req;\r |
| 219 | extern int32 stop_inst;\r |
| 220 | extern uint32 dma_ad[DMA_MAX];\r |
| 221 | extern int32 sim_switches;\r |
| 222 | \r |
| 223 | uint32 dp_cw1 = 0; /* cmd word 1 */\r |
| 224 | uint32 dp_cw2 = 0; /* cmd word 2 */\r |
| 225 | uint32 dp_fnc = 0; /* saved function */\r |
| 226 | uint32 dp_buf = 0; /* buffer */\r |
| 227 | uint32 dp_otas = 0; /* state */\r |
| 228 | uint32 dp_sta = 0; /* status */\r |
| 229 | uint32 dp_defint = 0; /* deferred seek int */\r |
| 230 | uint32 dp_ctype = TYPE_4651; /* controller type */\r |
| 231 | uint32 dp_dma = 0; /* DMA/DMC */\r |
| 232 | uint32 dp_eor = 0; /* end of range */\r |
| 233 | uint32 dp_xip = 0; /* transfer in prog */\r |
| 234 | uint32 dp_csum = 0; /* parity checksum */\r |
| 235 | uint32 dp_rptr = 0; /* start of record */\r |
| 236 | uint32 dp_wptr = 0; /* word ptr in record */\r |
| 237 | uint32 dp_bctr = 0; /* format bit cntr */\r |
| 238 | uint32 dp_gap = 0; /* format gap size */\r |
| 239 | uint32 dp_stopioe = 1; /* stop on error */\r |
| 240 | int32 dp_stime = 1000; /* seek per cylinder */\r |
| 241 | int32 dp_xtime = 10; /* xfer per word */\r |
| 242 | int32 dp_btime = 30; /* busy time */\r |
| 243 | uint16 dpxb[DP_TRKLEN]; /* track buffer */\r |
| 244 | \r |
| 245 | int32 dpio (int32 inst, int32 fnc, int32 dat, int32 dev);\r |
| 246 | t_stat dp_svc (UNIT *uptr);\r |
| 247 | t_stat dp_reset (DEVICE *dptr);\r |
| 248 | t_stat dp_attach (UNIT *uptr, char *cptr);\r |
| 249 | t_stat dp_settype (UNIT *uptr, int32 val, char *cptr, void *desc);\r |
| 250 | t_stat dp_showtype (FILE *st, UNIT *uptr, int32 val, void *desc);\r |
| 251 | t_stat dp_go (uint32 dma);\r |
| 252 | t_stat dp_go1 (uint32 dat);\r |
| 253 | t_stat dp_go2 (uint32 dat);\r |
| 254 | t_stat dp_rdtrk (UNIT *uptr, uint16 *buf, uint32 cyl, uint32 hd);\r |
| 255 | t_stat dp_wrtrk (UNIT *uptr, uint16 *buf, uint32 cyl, uint32 hd);\r |
| 256 | t_bool dp_findrec (uint32 addr);\r |
| 257 | t_stat dp_wrwd (UNIT *uptr, uint32 dat);\r |
| 258 | t_stat dp_wrdone (UNIT *uptr, uint32 flg);\r |
| 259 | t_stat dp_done (uint32 req, uint32 f);\r |
| 260 | t_stat dp_setformat (UNIT *uptr, int32 val, char *cptr, void *desc);\r |
| 261 | t_stat dp_showformat (FILE *st, UNIT *uptr, int32 val, void *desc);\r |
| 262 | \r |
| 263 | /* DP data structures\r |
| 264 | \r |
| 265 | dp_dev DP device descriptor\r |
| 266 | dp_unit DP unit list\r |
| 267 | dp_reg DP register list\r |
| 268 | dp_mod DP modifier list\r |
| 269 | */\r |
| 270 | \r |
| 271 | DIB dp_dib = { DP, DMC1, 1, &dpio };\r |
| 272 | \r |
| 273 | UNIT dp_unit[] = {\r |
| 274 | { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 275 | UNIT_ROABLE, CAP_4651) },\r |
| 276 | { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 277 | UNIT_ROABLE, CAP_4651) },\r |
| 278 | { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 279 | UNIT_ROABLE, CAP_4651) },\r |
| 280 | { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 281 | UNIT_ROABLE, CAP_4651) },\r |
| 282 | { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 283 | UNIT_ROABLE, CAP_4651) },\r |
| 284 | { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 285 | UNIT_ROABLE, CAP_4651) },\r |
| 286 | { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 287 | UNIT_ROABLE, CAP_4651) },\r |
| 288 | { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 289 | UNIT_ROABLE, CAP_4651) }\r |
| 290 | };\r |
| 291 | \r |
| 292 | REG dp_reg[] = {\r |
| 293 | { ORDATA (STA, dp_sta, 16) },\r |
| 294 | { ORDATA (BUF, dp_buf, 16) },\r |
| 295 | { ORDATA (FNC, dp_fnc, 4) },\r |
| 296 | { ORDATA (CW1, dp_cw1, 16) },\r |
| 297 | { ORDATA (CW2, dp_cw2, 16) },\r |
| 298 | { ORDATA (CSUM, dp_csum, 16) },\r |
| 299 | { FLDATA (BUSY, dp_sta, 15) },\r |
| 300 | { FLDATA (RDY, dp_sta, 14) },\r |
| 301 | { FLDATA (EOR, dp_eor, 0) },\r |
| 302 | { FLDATA (DEFINT, dp_defint, 0) },\r |
| 303 | { FLDATA (INTREQ, dev_int, INT_V_DP) },\r |
| 304 | { FLDATA (ENABLE, dev_enb, INT_V_DP) },\r |
| 305 | { BRDATA (TBUF, dpxb, 8, 16, DP_TRKLEN) },\r |
| 306 | { ORDATA (RPTR, dp_rptr, 11), REG_RO },\r |
| 307 | { ORDATA (WPTR, dp_wptr, 11), REG_RO },\r |
| 308 | { ORDATA (BCTR, dp_bctr, 15), REG_RO },\r |
| 309 | { ORDATA (GAP, dp_gap, 16), REG_RO },\r |
| 310 | { DRDATA (STIME, dp_stime, 24), REG_NZ + PV_LEFT },\r |
| 311 | { DRDATA (XTIME, dp_xtime, 24), REG_NZ + PV_LEFT },\r |
| 312 | { DRDATA (BTIME, dp_btime, 24), REG_NZ + PV_LEFT },\r |
| 313 | { FLDATA (CTYPE, dp_ctype, 0), REG_HRO },\r |
| 314 | { URDATA (UCYL, dp_unit[0].CYL, 10, 8, 0,\r |
| 315 | DP_NUMDRV, PV_LEFT | REG_HRO) },\r |
| 316 | { URDATA (UFNC, dp_unit[0].FNC, 8, 7, 0,\r |
| 317 | DP_NUMDRV, REG_HRO) },\r |
| 318 | { URDATA (CAPAC, dp_unit[0].capac, 10, T_ADDR_W, 0,\r |
| 319 | DP_NUMDRV, PV_LEFT | REG_HRO) },\r |
| 320 | { ORDATA (OTAS, dp_otas, 2), REG_HRO },\r |
| 321 | { ORDATA (XIP, dp_xip, 6), REG_HRO },\r |
| 322 | { ORDATA (CHAN, dp_dib.chan, 5), REG_HRO },\r |
| 323 | { FLDATA (STOP_IOE, dp_stopioe, 0) },\r |
| 324 | { NULL }\r |
| 325 | };\r |
| 326 | \r |
| 327 | MTAB dp_mod[] = {\r |
| 328 | { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },\r |
| 329 | { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },\r |
| 330 | { MTAB_XTD | MTAB_VDV, TYPE_4623, NULL, "4623",\r |
| 331 | &dp_settype, NULL, NULL },\r |
| 332 | { MTAB_XTD | MTAB_VDV, TYPE_4651, NULL, "4651",\r |
| 333 | &dp_settype, NULL, NULL },\r |
| 334 | { MTAB_XTD | MTAB_VDV, TYPE_4720, NULL, "4720",\r |
| 335 | &dp_settype, NULL, NULL },\r |
| 336 | { MTAB_XTD | MTAB_VDV, 0, "TYPE", NULL,\r |
| 337 | NULL, &dp_showtype, NULL },\r |
| 338 | { MTAB_XTD|MTAB_VDV, 0, NULL, "DMC",\r |
| 339 | &io_set_dmc, NULL, NULL },\r |
| 340 | { MTAB_XTD|MTAB_VDV, 0, NULL, "DMA",\r |
| 341 | &io_set_dma, NULL, NULL },\r |
| 342 | { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", NULL,\r |
| 343 | NULL, &io_show_chan, NULL },\r |
| 344 | { MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "FORMAT", "FORMAT",\r |
| 345 | &dp_setformat, &dp_showformat, NULL },\r |
| 346 | { 0 }\r |
| 347 | };\r |
| 348 | \r |
| 349 | DEVICE dp_dev = {\r |
| 350 | "DP", dp_unit, dp_reg, dp_mod,\r |
| 351 | DP_NUMDRV, 8, 24, 1, 8, 16,\r |
| 352 | NULL, NULL, &dp_reset,\r |
| 353 | NULL, &dp_attach, NULL,\r |
| 354 | &dp_dib, DEV_DISABLE\r |
| 355 | };\r |
| 356 | \r |
| 357 | /* IOT routines */\r |
| 358 | \r |
| 359 | int32 dpio (int32 inst, int32 fnc, int32 dat, int32 dev)\r |
| 360 | {\r |
| 361 | int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */\r |
| 362 | int32 u;\r |
| 363 | UNIT *uptr;\r |
| 364 | \r |
| 365 | switch (inst) { /* case on opcode */\r |
| 366 | \r |
| 367 | case ioOCP: /* OCP */\r |
| 368 | switch (fnc) { /* case on function */\r |
| 369 | \r |
| 370 | case FNC_SK0: case FNC_SEEK: case FNC_RCA: /* data transfer */\r |
| 371 | case FNC_UNL: case FNC_FMT: case FNC_RW:\r |
| 372 | dp_go (fnc); /* if !busy, start */\r |
| 373 | break;\r |
| 374 | \r |
| 375 | case FNC_STOP: /* stop transfer */\r |
| 376 | if (dp_xip) { /* transfer in prog? */\r |
| 377 | uptr = dp_dev.units + (dp_xip & XIP_UMSK); /* get unit */\r |
| 378 | sim_cancel (uptr); /* stop operation */\r |
| 379 | if (dp_xip & (XIP_WRT|XIP_FMT)) /* write or format? */\r |
| 380 | dp_wrdone (uptr, /* write track */\r |
| 381 | ((dp_xip & XIP_FMT) && /* check fmt state */\r |
| 382 | (uptr->FNC != (FNC_FMT|FNC_2ND)))?\r |
| 383 | STA_DTRER: 0);\r |
| 384 | else dp_done (1, dp_csum? STA_CSMER: 0);/* no, just clr busy */\r |
| 385 | dp_xip = 0; /* clear flag */\r |
| 386 | }\r |
| 387 | dp_otas = OTA_NOP; /* clear state */\r |
| 388 | dp_sta = dp_sta & ~STA_BUSY; /* clear busy */\r |
| 389 | break; \r |
| 390 | \r |
| 391 | case FNC_RDS: /* read status */\r |
| 392 | if (dp_sta & STA_BUSY) return dat; /* ignore if busy */\r |
| 393 | dp_sta = (dp_sta | STA_RDY) & ~(STA_MBZ | STA_ANYER);\r |
| 394 | if (dp_sta & STA_ALLERR) dp_sta = dp_sta | STA_ANYER;\r |
| 395 | dp_buf = dp_sta;\r |
| 396 | if (dp_dma && Q_DMA (ch)) SET_CH_REQ (ch); /* DMA? set chan req */\r |
| 397 | break;\r |
| 398 | \r |
| 399 | case FNC_DMA: /* set DMA/DMC */\r |
| 400 | dp_dma = 1;\r |
| 401 | break;\r |
| 402 | \r |
| 403 | case FNC_IOBUS: /* set IO bus */\r |
| 404 | dp_dma = 0;\r |
| 405 | break;\r |
| 406 | \r |
| 407 | case FNC_AKI: /* ack intr */\r |
| 408 | CLR_INT (INT_DP);\r |
| 409 | break;\r |
| 410 | \r |
| 411 | default: /* undefined */\r |
| 412 | return IOBADFNC (dat);\r |
| 413 | }\r |
| 414 | break;\r |
| 415 | \r |
| 416 | case ioINA: /* INA */\r |
| 417 | if (fnc) return IOBADFNC (dat); /* fnc 0 only */\r |
| 418 | if (dp_sta & STA_RDY) { /* ready? */\r |
| 419 | dp_sta = dp_sta & ~STA_RDY; /* clear ready */\r |
| 420 | return IOSKIP (dat | dp_buf); /* ret buf, skip */\r |
| 421 | }\r |
| 422 | break;\r |
| 423 | \r |
| 424 | case ioOTA: /* OTA */\r |
| 425 | if (fnc) return IOBADFNC (dat); /* fnc 0 only */\r |
| 426 | if (dp_sta & STA_RDY) { /* ready? */\r |
| 427 | dp_sta = dp_sta & ~STA_RDY; /* clear ready */\r |
| 428 | dp_buf = dat; /* store buf */\r |
| 429 | if (dp_otas == OTA_CW1) dp_go1 (dat); /* expecting CW1? */\r |
| 430 | else if (dp_otas == OTA_CW2) dp_go2 (dat); /* expecting CW2? */\r |
| 431 | return IOSKIP (dat);\r |
| 432 | }\r |
| 433 | break;\r |
| 434 | \r |
| 435 | case ioSKS: /* SKS */\r |
| 436 | u = 7; /* assume unit 7 */\r |
| 437 | switch (fnc) {\r |
| 438 | \r |
| 439 | case 000: /* ready */\r |
| 440 | if (dp_sta & STA_RDY) return IOSKIP (dat);\r |
| 441 | break;\r |
| 442 | \r |
| 443 | case 001: /* !interrupting */\r |
| 444 | if (!TST_INTREQ (INT_DP)) return IOSKIP (dat);\r |
| 445 | break;\r |
| 446 | \r |
| 447 | case 002: /* operational */\r |
| 448 | if (!(dp_sta & (STA_BUSY | STA_ALLERR))) return IOSKIP (dat);\r |
| 449 | break;\r |
| 450 | \r |
| 451 | case 003: /* !error */\r |
| 452 | if (!(dp_sta & STA_ALLERR)) return IOSKIP (dat);\r |
| 453 | break;\r |
| 454 | \r |
| 455 | case 004: /* !busy */\r |
| 456 | if (!(dp_sta & STA_BUSY)) return IOSKIP (dat);\r |
| 457 | break;\r |
| 458 | \r |
| 459 | case 011: case 012: case 013: /* !not seeking 0-6 */\r |
| 460 | case 014: case 015: case 016: case 017:\r |
| 461 | u = fnc - 011;\r |
| 462 | case 007: /* !not seeking 7 */\r |
| 463 | if (!sim_is_active (&dp_unit[u]) || /* quiescent? */\r |
| 464 | (dp_unit[u].FNC != (FNC_SEEK | FNC_2ND)))\r |
| 465 | return IOSKIP (dat); /* seeking sets late */\r |
| 466 | break;\r |
| 467 | }\r |
| 468 | break;\r |
| 469 | \r |
| 470 | case ioEND: /* end of range */\r |
| 471 | dp_eor = 1; /* transfer done */\r |
| 472 | break;\r |
| 473 | }\r |
| 474 | \r |
| 475 | return dat;\r |
| 476 | }\r |
| 477 | \r |
| 478 | /* Start new operation - recal, seek, read address, format, read/write */\r |
| 479 | \r |
| 480 | t_stat dp_go (uint32 fnc)\r |
| 481 | {\r |
| 482 | int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */\r |
| 483 | \r |
| 484 | if (dp_sta & STA_BUSY) return SCPE_OK; /* ignore if busy */\r |
| 485 | dp_fnc = fnc; /* save function */\r |
| 486 | dp_xip = 0; /* transfer not started */\r |
| 487 | dp_eor = 0; /* not end of range */\r |
| 488 | dp_csum = 0; /* init checksum */\r |
| 489 | dp_otas = OTA_CW1; /* expect CW1 */\r |
| 490 | dp_sta = (dp_sta | STA_BUSY | STA_RDY) & ~(STA_ALLERR | STA_EOR);\r |
| 491 | if (dp_dma && Q_DMA (ch)) { /* DMA and DMA channel? */\r |
| 492 | SET_CH_REQ (ch); /* set channel request */\r |
| 493 | dma_ad[ch] = dma_ad[ch] & ~DMA_IN; /* force output */\r |
| 494 | }\r |
| 495 | return SCPE_OK;\r |
| 496 | }\r |
| 497 | \r |
| 498 | /* Process command word 1 - recal, seek, read address, format, read/write */\r |
| 499 | \r |
| 500 | t_stat dp_go1 (uint32 dat)\r |
| 501 | {\r |
| 502 | int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */\r |
| 503 | uint32 u = CW1_GETUNIT (dat);\r |
| 504 | UNIT *uptr = dp_dev.units + u;\r |
| 505 | \r |
| 506 | dp_cw1 = dat; /* store CW1 */\r |
| 507 | dp_otas = OTA_NOP; /* assume no CW2 */\r |
| 508 | uptr->FNC = dp_fnc;\r |
| 509 | if (sim_is_active (uptr)) /* still seeking? */\r |
| 510 | return dp_done (1, STA_UNSER); /* unsafe */\r |
| 511 | if (!(uptr->flags & UNIT_ATT)) /* not attached? */\r |
| 512 | return dp_done (1, STA_OFLER); /* offline */\r |
| 513 | \r |
| 514 | switch (dp_fnc) { /* case on function */\r |
| 515 | \r |
| 516 | case FNC_SEEK: /* seek */\r |
| 517 | case FNC_SK0: /* recalibrate */\r |
| 518 | case FNC_UNL: /* unload */\r |
| 519 | sim_activate (uptr, dp_btime); /* quick timeout */\r |
| 520 | break;\r |
| 521 | \r |
| 522 | case FNC_FMT: /* format */\r |
| 523 | if (uptr->flags & UNIT_WPRT) /* write protect? */\r |
| 524 | return dp_done (1, STA_WPRER); /* stop now */\r |
| 525 | case FNC_RCA: /* read current addr */\r |
| 526 | dp_xip = u | XIP_SCHED; /* operation started */\r |
| 527 | sim_activate (uptr, dp_xtime * 10); /* rotation timeout */\r |
| 528 | break;\r |
| 529 | \r |
| 530 | case FNC_RW: /* read/write */\r |
| 531 | dp_otas = OTA_CW2; /* expect CW2 */\r |
| 532 | dp_sta = dp_sta | STA_RDY; /* set ready */\r |
| 533 | if (dp_dma && Q_DMA (ch)) SET_CH_REQ (ch); /* DMA? set chan request */\r |
| 534 | break;\r |
| 535 | }\r |
| 536 | \r |
| 537 | return SCPE_OK;\r |
| 538 | }\r |
| 539 | \r |
| 540 | /* Process command word 2 - read/write only */\r |
| 541 | \r |
| 542 | t_stat dp_go2 (uint32 dat)\r |
| 543 | {\r |
| 544 | uint32 u = CW1_GETUNIT (dp_cw1);\r |
| 545 | UNIT *uptr = dp_dev.units + u;\r |
| 546 | \r |
| 547 | dp_cw2 = dat; /* store CW2 */\r |
| 548 | dp_otas = OTA_NOP; /* normal state */\r |
| 549 | sim_activate (uptr, dp_xtime * 10); /* rotation timeout */\r |
| 550 | dp_xip = u | XIP_SCHED; /* operation started */\r |
| 551 | return SCPE_OK;\r |
| 552 | }\r |
| 553 | \r |
| 554 | /* Unit service */\r |
| 555 | \r |
| 556 | t_stat dp_svc (UNIT *uptr)\r |
| 557 | {\r |
| 558 | int32 dcyl = 0; /* assume recalibrate */\r |
| 559 | int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */\r |
| 560 | uint32 h = CW1_GETHEAD (dp_cw1); /* head */\r |
| 561 | int32 st;\r |
| 562 | uint32 i, offs, lnt, ming, tpos;\r |
| 563 | t_stat r;\r |
| 564 | \r |
| 565 | if (!(uptr->flags & UNIT_ATT)) { /* not attached? */\r |
| 566 | dp_done (1, STA_OFLER); /* offline */\r |
| 567 | return IORETURN (dp_stopioe, SCPE_UNATT);\r |
| 568 | }\r |
| 569 | \r |
| 570 | switch (uptr->FNC) { /* case on function */\r |
| 571 | \r |
| 572 | case FNC_SEEK: /* seek, need cyl */\r |
| 573 | offs = CW1_GETOFFS (dp_cw1); /* get offset */\r |
| 574 | if (dp_cw1 & CW1_DIR) dcyl = uptr->CYL - offs; /* get desired cyl */\r |
| 575 | else dcyl = uptr->CYL + offs;\r |
| 576 | if ((offs == 0) || (dcyl < 0) ||\r |
| 577 | (dcyl >= (int32) dp_tab[dp_ctype].cyl)) \r |
| 578 | return dp_done (1, STA_SEKER); /* bad seek? */\r |
| 579 | \r |
| 580 | case FNC_SK0: /* recalibrate */\r |
| 581 | dp_sta = dp_sta & ~STA_BUSY; /* clear busy */\r |
| 582 | uptr->FNC = FNC_SEEK | FNC_2ND; /* next state */\r |
| 583 | st = (abs (dcyl - uptr->CYL)) * dp_stime; /* schedule seek */\r |
| 584 | if (st == 0) st = dp_stime;\r |
| 585 | uptr->CYL = dcyl; /* put on cylinder */\r |
| 586 | sim_activate (uptr, st);\r |
| 587 | return SCPE_OK;\r |
| 588 | \r |
| 589 | case FNC_SEEK | FNC_2ND: /* seek, 2nd state */\r |
| 590 | if (dp_sta & STA_BUSY) dp_defint = 1; /* busy? queue intr */\r |
| 591 | else SET_INT (INT_DP); /* no, req intr */\r |
| 592 | return SCPE_OK;\r |
| 593 | \r |
| 594 | case FNC_UNL: /* unload */\r |
| 595 | detach_unit (uptr); /* detach unit */\r |
| 596 | return dp_done (0, 0); /* clear busy, no intr */\r |
| 597 | \r |
| 598 | case FNC_RCA: /* read current addr */\r |
| 599 | if (h >= dp_tab[dp_ctype].surf) /* invalid head? */\r |
| 600 | return dp_done (1, STA_ADRER); /* error */\r |
| 601 | if (r = dp_rdtrk (uptr, dpxb, uptr->CYL, h)) /* get track; error? */\r |
| 602 | return r;\r |
| 603 | dp_rptr = 0; /* init rec ptr */\r |
| 604 | if (dpxb[dp_rptr + REC_LNT] == 0) /* unformated? */\r |
| 605 | return dp_done (1, STA_ADRER); /* error */\r |
| 606 | tpos = (uint32) (fmod (sim_gtime () / (double) dp_xtime, DP_TRKLEN));\r |
| 607 | do { /* scan down track */\r |
| 608 | dp_buf = dpxb[dp_rptr + REC_ADDR]; /* get rec addr */\r |
| 609 | dp_rptr = dp_rptr + dpxb[dp_rptr + REC_LNT] + REC_OVHD;\r |
| 610 | } while ((dp_rptr < tpos) && (dpxb[dp_rptr + REC_LNT] != 0));\r |
| 611 | if (dp_dma) { /* DMA/DMC? */\r |
| 612 | if (Q_DMA (ch)) /* DMA? */\r |
| 613 | dma_ad[ch] = dma_ad[ch] | DMA_IN; /* force input */\r |
| 614 | SET_CH_REQ (ch); /* request chan */\r |
| 615 | }\r |
| 616 | return dp_done (1, STA_RDY); /* clr busy, set rdy */\r |
| 617 | \r |
| 618 | /* Formating takes place in five states:\r |
| 619 | \r |
| 620 | init - clear track buffer, start at first record\r |
| 621 | address - store address word\r |
| 622 | data - store data word(s) until end of range\r |
| 623 | pause - wait for gap word or stop command\r |
| 624 | gap - validate gap word, advance to next record\r |
| 625 | \r |
| 626 | Note that formating is stopped externally by an OCP command; the\r |
| 627 | track buffer is flushed at that point. If the stop does not occur\r |
| 628 | in the proper state (gap word received), a format error occurs.\r |
| 629 | */\r |
| 630 | \r |
| 631 | case FNC_FMT: /* format */\r |
| 632 | for (i = 0; i < DP_TRKLEN; i++) dpxb[i] = 0; /* clear track */\r |
| 633 | dp_xip = dp_xip | XIP_FMT; /* format in progress */\r |
| 634 | dp_rptr = 0; /* init record ptr */\r |
| 635 | dp_gap = 0; /* no gap before first */\r |
| 636 | dp_bctr = (uint32) (16.0 * dp_tab[dp_ctype].wrds); /* init bit cntr */\r |
| 637 | uptr->FNC = uptr->FNC | FNC_2ND; /* address state */\r |
| 638 | break; /* set up next word */\r |
| 639 | \r |
| 640 | case FNC_FMT | FNC_2ND: /* format, address word */\r |
| 641 | dp_wptr = 0; /* clear word ptr */\r |
| 642 | if (dp_bctr < (dp_gap + REC_OVHD_BITS + 16)) /* room for gap, record? */\r |
| 643 | return dp_wrdone (uptr, STA_FMTER); /* no, format error */\r |
| 644 | dp_bctr = dp_bctr - dp_gap - REC_OVHD_BITS; /* charge for gap, ovhd */\r |
| 645 | dpxb[dp_rptr + REC_ADDR] = dp_buf; /* store address */\r |
| 646 | uptr->FNC = FNC_FMT | FNC_3RD; /* data state */\r |
| 647 | if (dp_eor) { /* record done? */\r |
| 648 | dp_eor = 0; /* clear for restart */\r |
| 649 | if (dp_dma) SET_INT (INT_DP); /* DMA/DMC? intr */\r |
| 650 | }\r |
| 651 | break; /* set up next word */\r |
| 652 | \r |
| 653 | case FNC_FMT | FNC_3RD: /* format, data word */\r |
| 654 | if (dp_sta & STA_RDY) /* timing failure? */\r |
| 655 | return dp_wrdone (uptr, STA_DTRER); /* write trk, err */\r |
| 656 | else { /* no, have word */\r |
| 657 | if (dp_bctr < 16) /* room for it? */\r |
| 658 | return dp_wrdone (uptr, STA_FMTER); /* no, error */\r |
| 659 | dp_bctr = dp_bctr - 16; /* charge for word */\r |
| 660 | dp_csum = dp_csum ^ dp_buf; /* update checksum */\r |
| 661 | dpxb[dp_rptr + REC_DATA + dp_wptr] = dp_buf;/* store word */\r |
| 662 | dpxb[dp_rptr + REC_LNT]++; /* incr rec lnt */\r |
| 663 | dp_wptr++; /* incr word ptr */\r |
| 664 | }\r |
| 665 | if (dp_eor) { /* record done? */\r |
| 666 | dp_eor = 0; /* clear for restart */\r |
| 667 | if (dp_dma) SET_INT (INT_DP); /* DMA/DMC? intr */\r |
| 668 | dpxb[dp_rptr + REC_DATA + dp_wptr] = dp_csum; /* store checksum */\r |
| 669 | uptr->FNC = uptr->FNC | FNC_4TH; /* pause state */\r |
| 670 | sim_activate (uptr, 5 * dp_xtime); /* schedule pause */\r |
| 671 | return SCPE_OK; /* don't request word */\r |
| 672 | }\r |
| 673 | break; /* set up next word */\r |
| 674 | \r |
| 675 | case FNC_FMT | FNC_4TH: /* format, pause */\r |
| 676 | uptr->FNC = FNC_FMT | FNC_5TH; /* gap state */\r |
| 677 | break; /* request word */\r |
| 678 | \r |
| 679 | case FNC_FMT | FNC_5TH: /* format, gap word */\r |
| 680 | ming = ((16 * dp_wptr) + REC_OVHD_BITS) / 20; /* min 5% gap */\r |
| 681 | if (dp_buf < ming) /* too small? */\r |
| 682 | return dp_wrdone (uptr, STA_FMTER); /* yes, format error */\r |
| 683 | dp_rptr = dp_rptr + dp_wptr + REC_OVHD; /* next record */\r |
| 684 | uptr->FNC = FNC_FMT | FNC_2ND; /* address state */\r |
| 685 | if (dp_eor) { /* record done? */\r |
| 686 | dp_eor = 0; /* clear for restart */\r |
| 687 | if (dp_dma) SET_INT (INT_DP); /* DMA/DMC? intr */\r |
| 688 | }\r |
| 689 | dp_gap = dp_buf; /* save gap */\r |
| 690 | dp_csum = 0; /* clear checksum */\r |
| 691 | break; /* set up next word */\r |
| 692 | \r |
| 693 | /* Read and write take place in two states:\r |
| 694 | \r |
| 695 | init - read track into buffer, find record, validate parameters\r |
| 696 | data - (read) fetch data from buffer, stop on end of range\r |
| 697 | - (write) write data into buffer, flush on end of range\r |
| 698 | */\r |
| 699 | \r |
| 700 | case FNC_RW: /* read/write */\r |
| 701 | if (h >= dp_tab[dp_ctype].surf) /* invalid head? */\r |
| 702 | return dp_done (1, STA_ADRER); /* error */\r |
| 703 | if (r = dp_rdtrk (uptr, dpxb, uptr->CYL, h)) /* get track; error? */\r |
| 704 | return r;\r |
| 705 | if (!dp_findrec (dp_cw2)) /* find rec; error? */\r |
| 706 | return dp_done (1, STA_ADRER); /* address error */\r |
| 707 | if ((dpxb[dp_rptr + REC_LNT] >= (DP_TRKLEN - dp_rptr - REC_OVHD)) ||\r |
| 708 | (dpxb[dp_rptr + REC_EXT] >= REC_MAXEXT)) { /* bad lnt or ext? */\r |
| 709 | dp_done (1, STA_UNSER); /* stop simulation */\r |
| 710 | return STOP_DPFMT; /* bad format */\r |
| 711 | }\r |
| 712 | uptr->FNC = uptr->FNC | FNC_2ND; /* next state */\r |
| 713 | if (dp_cw1 & CW1_RW) { /* write? */\r |
| 714 | if (uptr->flags & UNIT_WPRT) /* write protect? */\r |
| 715 | return dp_done (1, STA_WPRER); /* error */\r |
| 716 | dp_xip = dp_xip | XIP_WRT; /* write in progress */\r |
| 717 | dp_sta = dp_sta | STA_RDY; /* set ready */\r |
| 718 | if (dp_dma) SET_CH_REQ (ch); /* if DMA/DMC, req chan */\r |
| 719 | }\r |
| 720 | else if (Q_DMA (ch)) /* read; DMA? */\r |
| 721 | dma_ad[ch] = dma_ad[ch] | DMA_IN; /* force input */\r |
| 722 | sim_activate (uptr, dp_xtime); /* schedule word */\r |
| 723 | dp_wptr = 0; /* init word pointer */\r |
| 724 | return SCPE_OK;\r |
| 725 | \r |
| 726 | case FNC_RW | FNC_2ND: /* read/write, word */\r |
| 727 | if (dp_cw1 & CW1_RW) { /* write? */\r |
| 728 | if (dp_sta & STA_RDY) /* timing failure? */\r |
| 729 | return dp_wrdone (uptr, STA_DTRER); /* yes, error */\r |
| 730 | if (r = dp_wrwd (uptr, dp_buf)) return r; /* wr word, error? */\r |
| 731 | if (dp_eor) { /* transfer done? */\r |
| 732 | dpxb[dp_rptr + REC_DATA + dp_wptr] = dp_csum;\r |
| 733 | return dp_wrdone (uptr, 0); /* clear busy, intr req */\r |
| 734 | }\r |
| 735 | }\r |
| 736 | else { /* read? */\r |
| 737 | lnt = dpxb[dp_rptr + REC_LNT] + dpxb[dp_rptr + REC_EXT];\r |
| 738 | dp_buf = dpxb[dp_rptr + REC_DATA + dp_wptr];/* current word */\r |
| 739 | dp_csum = dp_csum ^ dp_buf; /* xor to csum */\r |
| 740 | if ((dp_wptr > lnt) || dp_eor) /* transfer done? */\r |
| 741 | return dp_done (1,\r |
| 742 | (dp_csum? STA_CSMER: 0) |\r |
| 743 | ((dp_wptr >= lnt)? STA_EOR: 0));\r |
| 744 | if (dp_sta & STA_RDY) /* data buf full? */\r |
| 745 | return dp_done (1, STA_DTRER); /* no, underrun */\r |
| 746 | dp_wptr++; /* next word */\r |
| 747 | }\r |
| 748 | break;\r |
| 749 | \r |
| 750 | default:\r |
| 751 | return SCPE_IERR;\r |
| 752 | } /* end case */\r |
| 753 | \r |
| 754 | dp_sta = dp_sta | STA_RDY; /* set ready */\r |
| 755 | if (dp_dma) SET_CH_REQ (ch); /* if DMA/DMC, req chan */\r |
| 756 | sim_activate (uptr, dp_xtime); /* schedule word */\r |
| 757 | return SCPE_OK;\r |
| 758 | }\r |
| 759 | \r |
| 760 | /* Read track */\r |
| 761 | \r |
| 762 | t_stat dp_rdtrk (UNIT *uptr, uint16 *buf, uint32 c, uint32 h)\r |
| 763 | {\r |
| 764 | uint32 da = ((c * dp_tab[dp_ctype].surf) + h) * DP_TRKLEN;\r |
| 765 | int32 l;\r |
| 766 | \r |
| 767 | fseek (uptr->fileref, da * sizeof (uint16), SEEK_SET);\r |
| 768 | l = fxread (buf, sizeof (uint16), DP_TRKLEN, uptr->fileref);\r |
| 769 | for ( ; l < DP_TRKLEN; l++) buf[l] = 0;\r |
| 770 | if (ferror (uptr->fileref)) {\r |
| 771 | perror ("DP I/O error");\r |
| 772 | clearerr (uptr->fileref);\r |
| 773 | dp_done (1, STA_UNSER);\r |
| 774 | return SCPE_IOERR;\r |
| 775 | }\r |
| 776 | return SCPE_OK;\r |
| 777 | }\r |
| 778 | \r |
| 779 | /* Write track */\r |
| 780 | \r |
| 781 | t_stat dp_wrtrk (UNIT *uptr, uint16 *buf, uint32 c, uint32 h)\r |
| 782 | {\r |
| 783 | uint32 da = ((c * dp_tab[dp_ctype].surf) + h) * DP_TRKLEN;\r |
| 784 | \r |
| 785 | fseek (uptr->fileref, da * sizeof (uint16), SEEK_SET);\r |
| 786 | fxwrite (buf, sizeof (uint16), DP_TRKLEN, uptr->fileref);\r |
| 787 | if (ferror (uptr->fileref)) {\r |
| 788 | perror ("DP I/O error");\r |
| 789 | clearerr (uptr->fileref);\r |
| 790 | dp_done (1, STA_UNSER);\r |
| 791 | return SCPE_IOERR;\r |
| 792 | }\r |
| 793 | return SCPE_OK;\r |
| 794 | }\r |
| 795 | \r |
| 796 | /* Find record; true if found, false if not found */\r |
| 797 | \r |
| 798 | t_bool dp_findrec (uint32 addr)\r |
| 799 | {\r |
| 800 | dp_rptr = 0;\r |
| 801 | \r |
| 802 | do {\r |
| 803 | if (dpxb[dp_rptr + REC_LNT] == 0) return FALSE;\r |
| 804 | if (dpxb[dp_rptr + REC_LNT] >= DP_TRKLEN) return TRUE;\r |
| 805 | if (dpxb[dp_rptr + REC_ADDR] == addr) return TRUE;\r |
| 806 | dp_rptr = dp_rptr + dpxb[dp_rptr + REC_LNT] + REC_OVHD;\r |
| 807 | } while (dp_rptr < DP_TRKLEN);\r |
| 808 | return FALSE;\r |
| 809 | }\r |
| 810 | \r |
| 811 | /* Write next word to track buffer; return TRUE if ok, FALSE if next record trashed */\r |
| 812 | \r |
| 813 | t_stat dp_wrwd (UNIT *uptr, uint32 dat)\r |
| 814 | {\r |
| 815 | uint32 lnt = dpxb[dp_rptr + REC_LNT];\r |
| 816 | t_stat r;\r |
| 817 | \r |
| 818 | dp_csum = dp_csum ^ dat;\r |
| 819 | if (dp_wptr < lnt) {\r |
| 820 | dpxb[dp_rptr + REC_DATA + dp_wptr++] = dat;\r |
| 821 | return SCPE_OK;\r |
| 822 | }\r |
| 823 | if (dp_wptr < (lnt + REC_MAXEXT)) {\r |
| 824 | dpxb[dp_rptr + REC_EXT]++;\r |
| 825 | dpxb[dp_rptr + REC_DATA + dp_wptr++] = dat;\r |
| 826 | return SCPE_OK;\r |
| 827 | }\r |
| 828 | dpxb[dp_rptr + REC_DATA + dp_wptr] = dp_csum; /* write csum */\r |
| 829 | dpxb[dp_rptr + lnt + REC_OVHD] = 0; /* zap rest of track */\r |
| 830 | if (r = dp_wrdone (uptr, STA_UNSER)) return r; /* dump track */\r |
| 831 | return STOP_DPOVR;\r |
| 832 | } \r |
| 833 | \r |
| 834 | /* Write done, dump track, clear busy */\r |
| 835 | \r |
| 836 | t_stat dp_wrdone (UNIT *uptr, uint32 flg)\r |
| 837 | {\r |
| 838 | dp_done (1, flg);\r |
| 839 | return dp_wrtrk (uptr, dpxb, uptr->CYL, CW1_GETHEAD (dp_cw1));\r |
| 840 | }\r |
| 841 | \r |
| 842 | /* Clear busy, set errors, request interrupt if required */\r |
| 843 | \r |
| 844 | t_stat dp_done (uint32 req, uint32 flg)\r |
| 845 | {\r |
| 846 | dp_xip = 0; /* clear xfr in prog */\r |
| 847 | dp_sta = (dp_sta | flg) & ~(STA_BUSY | STA_MBZ); /* clear busy */\r |
| 848 | if (req || dp_defint) SET_INT (INT_DP); /* if req, set intr */\r |
| 849 | dp_defint = 0; /* clr def intr */\r |
| 850 | return SCPE_OK;\r |
| 851 | }\r |
| 852 | \r |
| 853 | /* Reset routine */\r |
| 854 | \r |
| 855 | t_stat dp_reset (DEVICE *dptr)\r |
| 856 | {\r |
| 857 | int32 i;\r |
| 858 | \r |
| 859 | dp_fnc = 0;\r |
| 860 | dp_cw1 = 0;\r |
| 861 | dp_cw2 = 0;\r |
| 862 | dp_sta = 0;\r |
| 863 | dp_buf = 0;\r |
| 864 | dp_xip = 0;\r |
| 865 | dp_eor = 0;\r |
| 866 | dp_dma = 0;\r |
| 867 | dp_csum = 0;\r |
| 868 | dp_rptr = 0;\r |
| 869 | dp_wptr = 0;\r |
| 870 | dp_bctr = 0;\r |
| 871 | dp_gap = 0;\r |
| 872 | dp_defint = 0;\r |
| 873 | for (i = 0; i < DP_NUMDRV; i++) { /* loop thru drives */\r |
| 874 | sim_cancel (&dp_unit[i]); /* cancel activity */\r |
| 875 | dp_unit[i].FNC = 0; /* clear function */\r |
| 876 | dp_unit[i].CYL = 0;\r |
| 877 | }\r |
| 878 | CLR_INT (INT_DP); /* clear int, enb */\r |
| 879 | CLR_ENB (INT_DP);\r |
| 880 | return SCPE_OK;\r |
| 881 | }\r |
| 882 | \r |
| 883 | /* Attach routine, test formating */\r |
| 884 | \r |
| 885 | t_stat dp_attach (UNIT *uptr, char *cptr)\r |
| 886 | {\r |
| 887 | t_stat r;\r |
| 888 | r = attach_unit (uptr, cptr);\r |
| 889 | if (r != SCPE_OK) return r;\r |
| 890 | return dp_showformat (stdout, uptr, 0, NULL);\r |
| 891 | }\r |
| 892 | \r |
| 893 | /* Set controller type */\r |
| 894 | \r |
| 895 | t_stat dp_settype (UNIT *uptr, int32 val, char *cptr, void *desc)\r |
| 896 | {\r |
| 897 | int32 i;\r |
| 898 | \r |
| 899 | if ((val < 0) || (val >= DP_NUMTYP) || (cptr != NULL)) return SCPE_ARG;\r |
| 900 | for (i = 0; i < DP_NUMDRV; i++) {\r |
| 901 | if (dp_unit[i].flags & UNIT_ATT) return SCPE_ALATT;\r |
| 902 | }\r |
| 903 | for (i = 0; i < DP_NUMDRV; i++)\r |
| 904 | dp_unit[i].capac = dp_tab[val].cap;\r |
| 905 | dp_ctype = val;\r |
| 906 | return SCPE_OK;\r |
| 907 | }\r |
| 908 | \r |
| 909 | /* Show controller type */\r |
| 910 | \r |
| 911 | t_stat dp_showtype (FILE *st, UNIT *uptr, int32 val, void *desc)\r |
| 912 | {\r |
| 913 | if (dp_ctype >= DP_NUMTYP) return SCPE_IERR;\r |
| 914 | fprintf (st, "%s", dp_tab[dp_ctype].name);\r |
| 915 | return SCPE_OK;\r |
| 916 | }\r |
| 917 | \r |
| 918 | /* Set drive format\r |
| 919 | \r |
| 920 | There is no standard format for record addresses. This routine\r |
| 921 | provides two schemes:\r |
| 922 | \r |
| 923 | -S sequential addressing (starting from 0)\r |
| 924 | default geometric addressing (8b: cylinder, 5b: head, 3b: sector)\r |
| 925 | \r |
| 926 | This routine also supports formatting by record count or word count:\r |
| 927 | \r |
| 928 | -R argument is records per track\r |
| 929 | default argument is words per record\r |
| 930 | \r |
| 931 | The relationship between words per record (W), bits per track (B),\r |
| 932 | and records per track (R), is as follows:\r |
| 933 | \r |
| 934 | W = (B / (R + ((R - 1) / 20))) - 16.5\r |
| 935 | \r |
| 936 | where (R - 1) / 20 is the "5% gap" and 16.5 is the overhead, in words,\r |
| 937 | per record.\r |
| 938 | */\r |
| 939 | \r |
| 940 | t_stat dp_setformat (UNIT *uptr, int32 val, char *cptr, void *desc)\r |
| 941 | {\r |
| 942 | uint32 h, c, cntr, rptr;\r |
| 943 | int32 i, nr, nw, inp;\r |
| 944 | uint16 tbuf[DP_TRKLEN];\r |
| 945 | float finp;\r |
| 946 | t_stat r;\r |
| 947 | \r |
| 948 | if (uptr == NULL) return SCPE_IERR;\r |
| 949 | if (cptr == NULL) return SCPE_ARG;\r |
| 950 | if (!(uptr->flags & UNIT_ATT)) return SCPE_UNATT;\r |
| 951 | inp = (int32) get_uint (cptr, 10, 2048, &r);\r |
| 952 | if (r != SCPE_OK) return r;\r |
| 953 | if (inp == 0) return SCPE_ARG;\r |
| 954 | finp = (float) inp;\r |
| 955 | if (sim_switches & SWMASK ('R')) { /* format records? */\r |
| 956 | nr = inp;\r |
| 957 | nw = (int32) ((dp_tab[dp_ctype].wrds / (finp + ((finp - 1.0) / 20.0))) - REC_OVHD_WRDS);\r |
| 958 | if (nw <= 0) return SCPE_ARG;\r |
| 959 | }\r |
| 960 | else {\r |
| 961 | nw = inp; /* format words */\r |
| 962 | nr = (int32) ((((20.0 * dp_tab[dp_ctype].wrds) / (finp + REC_OVHD_WRDS)) + 1.0) / 21.0);\r |
| 963 | if (nr <= 0) return SCPE_ARG;\r |
| 964 | }\r |
| 965 | printf ("Proposed format: records/track = %d, record size = %d\n", nr, nw);\r |
| 966 | if (!get_yn ("Formatting will destroy all data on this disk; proceed? [N]", FALSE))\r |
| 967 | return SCPE_OK;\r |
| 968 | for (c = cntr = 0; c < dp_tab[dp_ctype].cyl; c++) {\r |
| 969 | for (h = 0; h < dp_tab[dp_ctype].surf; h++) {\r |
| 970 | for (i = 0; i < DP_TRKLEN; i++) tbuf[i] = 0;\r |
| 971 | rptr = 0;\r |
| 972 | for (i = 0; i < nr; i++) {\r |
| 973 | tbuf[rptr + REC_LNT] = nw & DMASK;\r |
| 974 | if (sim_switches & SWMASK ('S'))\r |
| 975 | tbuf[rptr + REC_ADDR] = cntr++;\r |
| 976 | else tbuf[rptr + REC_ADDR] = (c << 8) + (h << 3) + i;\r |
| 977 | rptr = rptr + nw + REC_OVHD;\r |
| 978 | }\r |
| 979 | if (r = dp_wrtrk (uptr, tbuf, c, h)) return r;\r |
| 980 | }\r |
| 981 | }\r |
| 982 | printf ("Formatting complete\n");\r |
| 983 | return SCPE_OK;\r |
| 984 | }\r |
| 985 | \r |
| 986 | /* Show format */\r |
| 987 | \r |
| 988 | t_stat dp_showformat (FILE *st, UNIT *uptr, int32 val, void *desc)\r |
| 989 | {\r |
| 990 | uint32 c, h, rptr, rlnt, sec;\r |
| 991 | uint32 minrec = DP_TRKLEN;\r |
| 992 | uint32 maxrec = 0;\r |
| 993 | uint32 minsec = DP_TRKLEN;\r |
| 994 | uint32 maxsec = 0;\r |
| 995 | uint16 tbuf[DP_TRKLEN];\r |
| 996 | t_stat r;\r |
| 997 | \r |
| 998 | if (uptr == NULL) return SCPE_IERR;\r |
| 999 | if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT;\r |
| 1000 | for (c = 0; c < dp_tab[dp_ctype].cyl; c++) {\r |
| 1001 | for (h = 0; h < dp_tab[dp_ctype].surf; h++) {\r |
| 1002 | if (r = dp_rdtrk (uptr, tbuf, c, h)) return r;\r |
| 1003 | rptr = 0;\r |
| 1004 | rlnt = tbuf[rptr + REC_LNT];\r |
| 1005 | if (rlnt == 0) {\r |
| 1006 | if (c || h) fprintf (st,\r |
| 1007 | "Unformatted track, cyl = %d, head = %d\n", c, h);\r |
| 1008 | else fprintf (st, "Disk is unformatted\n");\r |
| 1009 | return SCPE_OK;\r |
| 1010 | }\r |
| 1011 | for (sec = 0; rlnt != 0; sec++) {\r |
| 1012 | if ((rptr + rlnt + REC_OVHD) >= DP_TRKLEN) {\r |
| 1013 | fprintf (st, "Invalid record length %d, cyl = %d, head = %d, sect = %d\n",\r |
| 1014 | rlnt, c, h, sec);\r |
| 1015 | return SCPE_OK;\r |
| 1016 | }\r |
| 1017 | if (tbuf[rptr + REC_EXT] >= REC_MAXEXT) {\r |
| 1018 | fprintf (st, "Invalid record extension %d, cyl = %d, head = %d, sect = %d\n",\r |
| 1019 | tbuf[rptr + REC_EXT], c, h, sec);\r |
| 1020 | return SCPE_OK;\r |
| 1021 | }\r |
| 1022 | if (rlnt > maxrec) maxrec = rlnt;\r |
| 1023 | if (rlnt < minrec) minrec = rlnt;\r |
| 1024 | rptr = rptr + rlnt + REC_OVHD;\r |
| 1025 | rlnt = tbuf[rptr + REC_LNT];\r |
| 1026 | }\r |
| 1027 | if (sec > maxsec) maxsec = sec;\r |
| 1028 | if (sec < minsec) minsec = sec;\r |
| 1029 | }\r |
| 1030 | }\r |
| 1031 | if ((minrec == maxrec) && (minsec == maxsec)) fprintf (st, \r |
| 1032 | "Valid fixed format, records/track = %d, record size = %d\n",\r |
| 1033 | minsec, minrec);\r |
| 1034 | else if (minrec == maxrec) fprintf (st,\r |
| 1035 | "Valid variable format, records/track = %d-%d, record size = %d\n",\r |
| 1036 | minsec, maxsec, minrec);\r |
| 1037 | else if (minsec == maxsec) fprintf (st,\r |
| 1038 | "Valid variable format, records/track = %d, record sizes = %d-%d\n",\r |
| 1039 | minsec, minrec, maxrec);\r |
| 1040 | else fprintf (st,\r |
| 1041 | "Valid variable format, records/track = %d-%d, record sizes = %d-%d\n",\r |
| 1042 | minsec, maxsec, minrec, maxrec);\r |
| 1043 | return SCPE_OK;\r |
| 1044 | }\r |