| 1 | /* sds_rad.c: SDS 940 fixed head disk simulator\r |
| 2 | \r |
| 3 | Copyright (c) 2001-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 | rad fixed head disk\r |
| 27 | \r |
| 28 | The fixed head disk is a head-per-track disk, with up to four disks. Each\r |
| 29 | disk is divided into two logical units. Reads and writes cannot cross logical\r |
| 30 | unit boundaries. The fixed head disk transfers 12b characters, rather than 6b\r |
| 31 | characters. To minimize overhead, the disk is buffered in memory.\r |
| 32 | */\r |
| 33 | \r |
| 34 | #include "sds_defs.h"\r |
| 35 | #include <math.h>\r |
| 36 | \r |
| 37 | /* Constants */\r |
| 38 | \r |
| 39 | #define RAD_NUMWD 64 /* words/sector */\r |
| 40 | #define RAD_NUMSC 64 /* sectors/track */\r |
| 41 | #define RAD_NUMTR 64 /* tracks/log unit */\r |
| 42 | #define RAD_NUMLU 8 /* log units/ctrl */\r |
| 43 | #define RAD_SCSIZE (RAD_NUMLU*RAD_NUMTR*RAD_NUMSC) /* sectors/disk */\r |
| 44 | #define RAD_AMASK (RAD_SCSIZE - 1) /* sec addr mask */\r |
| 45 | #define RAD_SIZE (RAD_SCSIZE * RAD_NUMWD) /* words/disk */\r |
| 46 | #define RAD_GETLUN(x) ((x) / (RAD_NUMTR * RAD_NUMSC))\r |
| 47 | #define RAD_SCMASK (RAD_NUMSC - 1) /* sector mask */\r |
| 48 | #define RAD_TRSCMASK ((RAD_NUMSC * RAD_NUMTR) - 1) /* track/sec mask */\r |
| 49 | \r |
| 50 | #define GET_SECTOR(x) ((int) fmod (sim_gtime() / ((double) (x)), \\r |
| 51 | ((double) RAD_NUMSC)))\r |
| 52 | \r |
| 53 | extern uint32 xfr_req;\r |
| 54 | extern uint32 alert;\r |
| 55 | extern int32 stop_invins, stop_invdev, stop_inviop;\r |
| 56 | int32 rad_err = 0; /* error */\r |
| 57 | int32 rad_nobi = 0; /* !incr x track */\r |
| 58 | int32 rad_da = 0; /* disk address */\r |
| 59 | int32 rad_sba = 0; /* sec byte addr */\r |
| 60 | int32 rad_wrp = 0; /* write prot */\r |
| 61 | int32 rad_time = 2; /* time per 12b */\r |
| 62 | int32 rad_stopioe = 1; /* stop on error */\r |
| 63 | DSPT rad_tplt[] = { /* template */\r |
| 64 | { 1, 0 },\r |
| 65 | { 1, DEV_OUT },\r |
| 66 | { 0, 0 }\r |
| 67 | };\r |
| 68 | \r |
| 69 | DEVICE rad_dev;\r |
| 70 | t_stat rad_svc (UNIT *uptr);\r |
| 71 | t_stat rad_reset (DEVICE *dptr);\r |
| 72 | t_stat rad_fill (int32 sba);\r |
| 73 | void rad_end_op (int32 fl);\r |
| 74 | int32 rad_adjda (int32 sba, int32 inc);\r |
| 75 | t_stat rad (uint32 fnc, uint32 inst, uint32 *dat);\r |
| 76 | \r |
| 77 | /* RAD data structures\r |
| 78 | \r |
| 79 | rad_dev device descriptor\r |
| 80 | rad_unit unit descriptor\r |
| 81 | rad_reg register list\r |
| 82 | */\r |
| 83 | \r |
| 84 | DIB rad_dib = { CHAN_E, DEV_RAD, XFR_RAD, rad_tplt, &rad };\r |
| 85 | \r |
| 86 | UNIT rad_unit = {\r |
| 87 | UDATA (&rad_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF,\r |
| 88 | RAD_SIZE)\r |
| 89 | };\r |
| 90 | \r |
| 91 | REG rad_reg[] = {\r |
| 92 | { ORDATA (DA, rad_da, 15) },\r |
| 93 | { GRDATA (SA, rad_sba, 8, 6, 1) },\r |
| 94 | { FLDATA (BP, rad_sba, 0) },\r |
| 95 | { FLDATA (XFR, xfr_req, XFR_V_RAD) },\r |
| 96 | { FLDATA (NOBD, rad_nobi, 0) },\r |
| 97 | { FLDATA (ERR, rad_err, 0) },\r |
| 98 | { ORDATA (PROT, rad_wrp, 8) },\r |
| 99 | { DRDATA (TIME, rad_time, 24), REG_NZ + PV_LEFT },\r |
| 100 | { FLDATA (STOP_IOE, rad_stopioe, 0) },\r |
| 101 | { NULL }\r |
| 102 | };\r |
| 103 | \r |
| 104 | MTAB rad_mod[] = {\r |
| 105 | { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL",\r |
| 106 | &set_chan, &show_chan, NULL },\r |
| 107 | { 0 }\r |
| 108 | };\r |
| 109 | \r |
| 110 | DEVICE rad_dev = {\r |
| 111 | "RAD", &rad_unit, rad_reg, rad_mod,\r |
| 112 | 1, 8, 21, 1, 8, 24,\r |
| 113 | NULL, NULL, &rad_reset,\r |
| 114 | NULL, NULL, NULL,\r |
| 115 | &rad_dib, DEV_DISABLE\r |
| 116 | };\r |
| 117 | \r |
| 118 | /* Fixed head disk routine\r |
| 119 | \r |
| 120 | conn - inst = EOM0, dat = NULL\r |
| 121 | eom1 - inst = EOM1, dat = NULL\r |
| 122 | sks - inst = SKS, dat = ptr to result\r |
| 123 | disc - inst = device number, dat = NULL\r |
| 124 | wreor - inst = device number, dat = NULL\r |
| 125 | read - inst = device number, dat = ptr to data\r |
| 126 | write - inst = device number, dat = ptr to result\r |
| 127 | */\r |
| 128 | \r |
| 129 | t_stat rad (uint32 fnc, uint32 inst, uint32 *dat)\r |
| 130 | {\r |
| 131 | int32 t, lun, new_ch;\r |
| 132 | uint32 p;\r |
| 133 | uint32 *fbuf = rad_unit.filebuf;\r |
| 134 | \r |
| 135 | switch (fnc) { /* case function */\r |
| 136 | \r |
| 137 | case IO_CONN: /* connect */\r |
| 138 | new_ch = I_GETEOCH (inst); /* get new chan */\r |
| 139 | if (new_ch != rad_dib.chan) return SCPE_IERR; /* wrong chan? */\r |
| 140 | if (CHC_GETCPW (inst) > 1) return STOP_INVIOP; /* 1-2 char/word? */\r |
| 141 | if (sim_is_active (&rad_unit) || (alert == POT_RADA)) /* protocol viol? */\r |
| 142 | return STOP_INVIOP;\r |
| 143 | rad_err = 0; /* clr error */\r |
| 144 | rad_sba = 0; /* clr sec bptr */\r |
| 145 | chan_set_flag (rad_dib.chan, CHF_12B); /* 12B mode */\r |
| 146 | t = (rad_da & RAD_SCMASK) - GET_SECTOR (rad_time * RAD_NUMWD);\r |
| 147 | if (t <= 0) t = t + RAD_NUMSC; /* seek */\r |
| 148 | sim_activate (&rad_unit, t * rad_time * (RAD_NUMWD / 2));\r |
| 149 | xfr_req = xfr_req & ~XFR_RAD; /* clr xfr flg */\r |
| 150 | break;\r |
| 151 | \r |
| 152 | case IO_EOM1: /* EOM mode 1 */\r |
| 153 | new_ch = I_GETEOCH (inst); /* get new chan */\r |
| 154 | if (new_ch != rad_dib.chan) return SCPE_IERR; /* wrong chan? */\r |
| 155 | if ((inst & 00600) == 00200) alert = POT_RADS; /* alert for sec */ \r |
| 156 | else if ((inst & 06600) == 0) { /* alert for addr */\r |
| 157 | if (sim_is_active (&rad_unit)) rad_err = 1; /* busy? */\r |
| 158 | else {\r |
| 159 | rad_nobi = (inst & 01000)? 1: 0; /* save inc type */\r |
| 160 | alert = POT_RADA; /* set alert */\r |
| 161 | }\r |
| 162 | }\r |
| 163 | break;\r |
| 164 | \r |
| 165 | case IO_DISC: /* disconnect */\r |
| 166 | rad_end_op (0); /* normal term */\r |
| 167 | if (inst & DEV_OUT) return rad_fill (rad_sba); /* fill write */\r |
| 168 | break;\r |
| 169 | \r |
| 170 | case IO_WREOR: /* write eor */\r |
| 171 | rad_end_op (CHF_EOR); /* eor term */\r |
| 172 | return rad_fill (rad_sba); /* fill write */\r |
| 173 | \r |
| 174 | case IO_SKS: /* SKS */\r |
| 175 | new_ch = I_GETSKCH (inst); /* sks chan */\r |
| 176 | if (new_ch != rad_dib.chan) return SCPE_IERR; /* wrong chan? */\r |
| 177 | t = I_GETSKCND (inst); /* sks cond */\r |
| 178 | lun = RAD_GETLUN (rad_da);\r |
| 179 | if (((t == 000) && !sim_is_active (&rad_unit)) || /* 10026: ready */\r |
| 180 | ((t == 004) && !rad_err) || /* 11026: !err */\r |
| 181 | ((t == 014) && !(rad_wrp & (1 << lun)))) /* 13026: !wrprot */\r |
| 182 | *dat = 1;\r |
| 183 | break;\r |
| 184 | \r |
| 185 | case IO_READ: /* read */\r |
| 186 | p = (rad_da * RAD_NUMWD) + (rad_sba >> 1); /* buf wd addr */\r |
| 187 | xfr_req = xfr_req & ~XFR_RAD; /* clr xfr req */\r |
| 188 | if ((rad_unit.flags & UNIT_BUF) == 0) { /* not buffered? */\r |
| 189 | rad_end_op (CHF_ERR | CHF_EOR); /* set rad err */\r |
| 190 | CRETIOE (rad_stopioe, SCPE_UNATT);\r |
| 191 | }\r |
| 192 | if (p >= rad_unit.capac) { /* end of disk? */\r |
| 193 | rad_end_op (CHF_ERR | CHF_EOR); /* set rad err */\r |
| 194 | return SCPE_OK;\r |
| 195 | }\r |
| 196 | if (rad_sba & 1) *dat = fbuf[p] & 07777; /* odd byte? */\r |
| 197 | else *dat = (fbuf[p] >> 12) & 07777; /* even */\r |
| 198 | rad_sba = rad_adjda (rad_sba, 1); /* next byte */\r |
| 199 | break;\r |
| 200 | \r |
| 201 | case IO_WRITE:\r |
| 202 | p = (rad_da * RAD_NUMWD) + (rad_sba >> 1);\r |
| 203 | xfr_req = xfr_req & ~XFR_RAD; /* clr xfr req */\r |
| 204 | if ((rad_unit.flags & UNIT_BUF) == 0) { /* not buffered? */\r |
| 205 | rad_end_op (CHF_ERR | CHF_EOR); /* set rad err */\r |
| 206 | CRETIOE (rad_stopioe, SCPE_UNATT);\r |
| 207 | }\r |
| 208 | if ((p >= rad_unit.capac) || /* end of disk? */\r |
| 209 | (rad_wrp & (1 << RAD_GETLUN (rad_da)))) { /* write prot? */\r |
| 210 | rad_end_op (CHF_ERR | CHF_EOR); /* set rad err */\r |
| 211 | return SCPE_OK;\r |
| 212 | }\r |
| 213 | if (rad_sba & 1) fbuf[p] = fbuf[p] | (*dat & 07777); /* odd byte? */\r |
| 214 | else fbuf[p] = (*dat & 07777) << 12; /* even */\r |
| 215 | if (p >= rad_unit.hwmark) rad_unit.hwmark = p + 1; /* mark hiwater */\r |
| 216 | rad_sba = rad_adjda (rad_sba, 1); /* next byte */\r |
| 217 | break;\r |
| 218 | \r |
| 219 | default:\r |
| 220 | CRETINS;\r |
| 221 | }\r |
| 222 | \r |
| 223 | return SCPE_OK;\r |
| 224 | }\r |
| 225 | \r |
| 226 | /* PIN routine */\r |
| 227 | \r |
| 228 | t_stat pin_rads (uint32 num, uint32 *dat)\r |
| 229 | {\r |
| 230 | *dat = GET_SECTOR (rad_time * RAD_NUMWD); /* ret curr sec */\r |
| 231 | return SCPE_OK;\r |
| 232 | }\r |
| 233 | \r |
| 234 | /* POT routine */\r |
| 235 | \r |
| 236 | t_stat pot_rada (uint32 num, uint32 *dat)\r |
| 237 | {\r |
| 238 | rad_da = (*dat) & RAD_AMASK; /* save dsk addr */\r |
| 239 | return SCPE_OK;\r |
| 240 | }\r |
| 241 | \r |
| 242 | /* Unit service and read/write */\r |
| 243 | \r |
| 244 | t_stat rad_svc (UNIT *uptr)\r |
| 245 | {\r |
| 246 | xfr_req = xfr_req | XFR_RAD; /* set xfr req */\r |
| 247 | sim_activate (&rad_unit, rad_time); /* activate */\r |
| 248 | return SCPE_OK;\r |
| 249 | }\r |
| 250 | \r |
| 251 | /* Fill incomplete sector */\r |
| 252 | \r |
| 253 | t_stat rad_fill (int32 sba)\r |
| 254 | {\r |
| 255 | uint32 p = rad_da * RAD_NUMWD;\r |
| 256 | uint32 *fbuf = rad_unit.filebuf;\r |
| 257 | int32 wa = (sba + 1) >> 1; /* whole words */\r |
| 258 | \r |
| 259 | if (sba && (p < rad_unit.capac)) { /* fill needed? */\r |
| 260 | for ( ; wa < RAD_NUMWD; wa++) fbuf[p + wa] = 0;\r |
| 261 | if ((p + wa) >= rad_unit.hwmark) rad_unit.hwmark = p + wa + 1;\r |
| 262 | rad_adjda (sba, RAD_NUMWD - 1); /* inc da */\r |
| 263 | }\r |
| 264 | return SCPE_OK;\r |
| 265 | }\r |
| 266 | \r |
| 267 | /* Adjust disk address */\r |
| 268 | \r |
| 269 | int32 rad_adjda (int32 sba, int32 inc)\r |
| 270 | {\r |
| 271 | sba = sba + inc;\r |
| 272 | if (rad_sba >= (RAD_NUMWD * 2)) { /* next sector? */\r |
| 273 | if (rad_nobi) rad_da = (rad_da & ~RAD_SCMASK) + /* within band? */\r |
| 274 | ((rad_da + 1) & RAD_SCMASK);\r |
| 275 | else rad_da = (rad_da & ~RAD_TRSCMASK) + /* cross band */\r |
| 276 | ((rad_da + 1) & RAD_TRSCMASK);\r |
| 277 | sba = 0; /* start new sec */\r |
| 278 | }\r |
| 279 | return sba;\r |
| 280 | }\r |
| 281 | \r |
| 282 | /* Terminate disk operation */\r |
| 283 | \r |
| 284 | void rad_end_op (int32 fl)\r |
| 285 | {\r |
| 286 | if (fl) chan_set_flag (rad_dib.chan, fl); /* set flags */\r |
| 287 | xfr_req = xfr_req & ~XFR_RAD; /* clear xfr */\r |
| 288 | sim_cancel (&rad_unit); /* stop */\r |
| 289 | if (fl & CHF_ERR) { /* error? */\r |
| 290 | chan_disc (rad_dib.chan); /* disconnect */\r |
| 291 | rad_err = 1; /* set rad err */\r |
| 292 | }\r |
| 293 | return;\r |
| 294 | }\r |
| 295 | \r |
| 296 | /* Reset routine */\r |
| 297 | \r |
| 298 | t_stat rad_reset (DEVICE *dptr)\r |
| 299 | {\r |
| 300 | chan_disc (rad_dib.chan); /* disconnect */\r |
| 301 | rad_nobi = 0; /* clear state */\r |
| 302 | rad_da = 0;\r |
| 303 | rad_sba = 0;\r |
| 304 | xfr_req = xfr_req & ~XFR_RAD; /* clr xfr req */\r |
| 305 | sim_cancel (&rad_unit); /* deactivate */\r |
| 306 | return SCPE_OK;\r |
| 307 | }\r |