| 1 | /* i1620_dp.c: IBM 1311 disk simulator\r |
| 2 | \r |
| 3 | Copyright (c) 2002-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 1311 disk pack\r |
| 27 | \r |
| 28 | The 1311 disk pack has 100 cylinders, 10 tracks/cylinder, 20 sectors/track.\r |
| 29 | Each sector contains 105 characters of information:\r |
| 30 | \r |
| 31 | 5c sector address\r |
| 32 | 100c sector data\r |
| 33 | \r |
| 34 | By default, a sector's address field will be '00000', which is interpreted\r |
| 35 | to mean the implied sector number that would be in place if the disk pack\r |
| 36 | had been formatted with sequential sector numbers.\r |
| 37 | \r |
| 38 | 18-Oct-02 RMS Fixed bug in error testing (found by Hans Pufal)\r |
| 39 | */\r |
| 40 | \r |
| 41 | #include "i1620_defs.h"\r |
| 42 | \r |
| 43 | #define DP_NUMDR 4 /* #drives */\r |
| 44 | #define UNIT_V_WAE (UNIT_V_UF + 0) /* write addr enab */\r |
| 45 | #define UNIT_WAE (1 << UNIT_V_WAE)\r |
| 46 | \r |
| 47 | /* Disk format */\r |
| 48 | \r |
| 49 | #define DP_ADDR 5 /* address */\r |
| 50 | #define DP_DATA 100 /* data */\r |
| 51 | #define DP_NUMCH (DP_ADDR + DP_DATA)\r |
| 52 | \r |
| 53 | #define DP_NUMSC 20 /* #sectors */\r |
| 54 | #define DP_NUMSF 10 /* #surfaces */\r |
| 55 | #define DP_NUMCY 100 /* #cylinders */\r |
| 56 | #define DP_TOTSC (DP_NUMCY * DP_NUMSF * DP_NUMSC)\r |
| 57 | #define DP_SIZE (DP_TOTSC * DP_NUMCH)\r |
| 58 | \r |
| 59 | /* Disk control field */\r |
| 60 | \r |
| 61 | #define DCF_DRV 0 /* drive select */\r |
| 62 | #define DCF_SEC 1 /* sector addr */\r |
| 63 | #define DCF_SEC_LEN 5\r |
| 64 | #define DCF_CNT (DCF_SEC + DCF_SEC_LEN) /* sector count */\r |
| 65 | #define DCF_CNT_LEN 3\r |
| 66 | #define DCF_ADR (DCF_CNT + DCF_CNT_LEN) /* buffer address */\r |
| 67 | #define DCF_ADR_LEN 5\r |
| 68 | #define DCF_LEN (DCF_ADR + DCF_ADR_LEN)\r |
| 69 | \r |
| 70 | /* Functions */\r |
| 71 | \r |
| 72 | #define FNC_SEEK 1 /* seek */\r |
| 73 | #define FNC_SEC 0 /* sectors */\r |
| 74 | #define FNC_WCH 1 /* write check */\r |
| 75 | #define FNC_NRL 2 /* no rec lnt chk */\r |
| 76 | #define FNC_TRK 4 /* tracks */\r |
| 77 | #define FNC_WRI 8 /* write offset */\r |
| 78 | \r |
| 79 | #define CYL u3 /* current cylinder */\r |
| 80 | \r |
| 81 | extern uint8 M[MAXMEMSIZE]; /* memory */\r |
| 82 | extern uint8 ind[NUM_IND];\r |
| 83 | extern UNIT cpu_unit;\r |
| 84 | \r |
| 85 | int32 dp_stop = 1; /* disk err stop */\r |
| 86 | uint32 dp_ba = 0; /* buffer addr */\r |
| 87 | \r |
| 88 | t_stat dp_reset (DEVICE *dptr);\r |
| 89 | t_stat dp_rdadr (UNIT *uptr, int32 sec, int32 qnr, int32 qwc);\r |
| 90 | t_stat dp_rdsec (UNIT *uptr, int32 sec, int32 qnr, int32 qwc);\r |
| 91 | t_stat dp_wradr (UNIT *uptr, int32 sec, int32 qnr);\r |
| 92 | t_stat dp_wrsec (UNIT *uptr, int32 sec, int32 qnr);\r |
| 93 | int32 dp_fndsec (UNIT *uptr, int32 sec, t_bool rd);\r |
| 94 | t_stat dp_nexsec (UNIT *uptr, int32 sec, int32 psec, t_bool rd);\r |
| 95 | t_bool dp_zeroad (uint8 *ap);\r |
| 96 | int32 dp_cvt_ad (uint8 *ap);\r |
| 97 | int32 dp_trkop (int32 drv, int32 sec);\r |
| 98 | int32 dp_cvt_bcd (uint32 ad, int32 len);\r |
| 99 | void dp_fill (UNIT *uptr, uint32 da, int32 cnt);\r |
| 100 | t_stat dp_tstgm (uint32 c, int32 qnr);\r |
| 101 | \r |
| 102 | /* DP data structures\r |
| 103 | \r |
| 104 | dp_dev DP device descriptor\r |
| 105 | dp_unit DP unit list\r |
| 106 | dp_reg DP register list\r |
| 107 | dp_mod DP modifier list\r |
| 108 | */\r |
| 109 | \r |
| 110 | UNIT dp_unit[] = {\r |
| 111 | { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE +\r |
| 112 | UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) },\r |
| 113 | { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE +\r |
| 114 | UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) },\r |
| 115 | { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE +\r |
| 116 | UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) },\r |
| 117 | { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE +\r |
| 118 | UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) }\r |
| 119 | };\r |
| 120 | \r |
| 121 | REG dp_reg[] = {\r |
| 122 | { FLDATA (ADCHK, ind[IN_DACH], 0) },\r |
| 123 | { FLDATA (WLRC, ind[IN_DWLR], 0) },\r |
| 124 | { FLDATA (CYLO, ind[IN_DCYO], 0) },\r |
| 125 | { FLDATA (ERR, ind[IN_DERR], 0) },\r |
| 126 | { FLDATA (DPSTOP, dp_stop, 0) },\r |
| 127 | { URDATA (CYL, dp_unit[0].CYL, 10, 8, 0,\r |
| 128 | DP_NUMDR, PV_LEFT + REG_RO) },\r |
| 129 | { NULL }\r |
| 130 | };\r |
| 131 | \r |
| 132 | MTAB dp_mod[] = {\r |
| 133 | { UNIT_WAE, 0, "write address disabled", "ADDROFF", NULL },\r |
| 134 | { UNIT_WAE, UNIT_WAE, "write address enabled", "ADDRON", NULL }, \r |
| 135 | { 0 }\r |
| 136 | };\r |
| 137 | \r |
| 138 | DEVICE dp_dev = {\r |
| 139 | "DP", dp_unit, dp_reg, dp_mod,\r |
| 140 | DP_NUMDR, 10, 21, 1, 16, 5,\r |
| 141 | NULL, NULL, &dp_reset,\r |
| 142 | NULL, NULL, NULL\r |
| 143 | };\r |
| 144 | \r |
| 145 | /* Disk IO routine */\r |
| 146 | \r |
| 147 | t_stat dp (uint32 op, uint32 pa, uint32 f0, uint32 f1)\r |
| 148 | {\r |
| 149 | int32 drv, sa, sec, psec, cnt, qwc, qnr, t;\r |
| 150 | UNIT *uptr;\r |
| 151 | t_stat r;\r |
| 152 | \r |
| 153 | if (pa & 1) return STOP_INVDCF; /* dcf must be even */\r |
| 154 | ind[IN_DACH] = ind[IN_DWLR] = 0; /* clr indicators */\r |
| 155 | ind[IN_DERR] = ind[IN_DCYO] = 0;\r |
| 156 | sa = ADDR_A (pa, DCF_SEC); /* ptr to sector */\r |
| 157 | if (((dp_unit[0].flags & UNIT_DIS) == 0) && /* only drive 0? */\r |
| 158 | (dp_unit[1].flags & UNIT_DIS) &&\r |
| 159 | (dp_unit[2].flags & UNIT_DIS) &&\r |
| 160 | (dp_unit[3].flags & UNIT_DIS)) drv = 0; /* ignore drv select */\r |
| 161 | else drv = (((M[pa] & 1)? M[pa]: M[sa]) & 0xE) >> 1; /* drive # */\r |
| 162 | if (drv >= DP_NUMDR) return STOP_INVDRV; /* invalid? */\r |
| 163 | uptr = dp_dev.units + drv; /* get unit ptr */\r |
| 164 | if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */\r |
| 165 | ind[IN_DERR] = 1; /* no, error */\r |
| 166 | CRETIOE (dp_stop, SCPE_UNATT);\r |
| 167 | }\r |
| 168 | \r |
| 169 | sec = dp_cvt_bcd (sa, DCF_SEC_LEN); /* cvt sector */\r |
| 170 | if ((sec < 0) || (sec >= (DP_NUMDR * DP_TOTSC))) /* bad sector? */\r |
| 171 | return STOP_INVDSC;\r |
| 172 | if (op == OP_K) { /* seek? */\r |
| 173 | if (f1 != FNC_SEEK) return STOP_INVFNC; /* really? */\r |
| 174 | uptr->CYL = (sec / (DP_NUMSF * DP_NUMSC)) % /* set cyl # */\r |
| 175 | DP_NUMCY;\r |
| 176 | return SCPE_OK; /* done! */\r |
| 177 | }\r |
| 178 | \r |
| 179 | cnt = dp_cvt_bcd (ADDR_A (pa, DCF_CNT), DCF_CNT_LEN); /* get count */\r |
| 180 | t = dp_cvt_bcd (ADDR_A (pa, DCF_ADR), DCF_ADR_LEN); /* get address */\r |
| 181 | if ((t < 0) || (t & 1)) return STOP_INVDBA; /* bad address? */\r |
| 182 | dp_ba = t; /* save addr */\r |
| 183 | \r |
| 184 | if (f1 >= FNC_WRI) return STOP_INVFNC; /* invalid func? */\r |
| 185 | if (op == OP_RN) qwc = f1 & FNC_WCH; /* read? set wch */\r |
| 186 | else if (op == OP_WN) { /* write? */\r |
| 187 | if (op & FNC_WCH) return STOP_INVFNC; /* cant check */\r |
| 188 | f1 = f1 + FNC_WRI; /* offset fnc */\r |
| 189 | }\r |
| 190 | else return STOP_INVFNC; /* not R or W */\r |
| 191 | qnr = f1 & FNC_NRL; /* no rec check? */\r |
| 192 | \r |
| 193 | switch (f1 & ~(FNC_WCH | FNC_NRL)) { /* case on function */\r |
| 194 | \r |
| 195 | case FNC_SEC: /* read sectors */\r |
| 196 | if (cnt <= 0) return STOP_INVDCN; /* bad count? */\r |
| 197 | psec = dp_fndsec (uptr, sec, TRUE); /* find sector */\r |
| 198 | if (psec < 0) CRETIOE (dp_stop, STOP_DACERR); /* error? */\r |
| 199 | do { /* loop on count */\r |
| 200 | if (r = dp_rdsec (uptr, psec, qnr, qwc)) /* read sector */\r |
| 201 | break;\r |
| 202 | sec++; psec++; /* next sector */\r |
| 203 | } while ((--cnt > 0) &&\r |
| 204 | ((r = dp_nexsec (uptr, sec, psec, TRUE)) == SCPE_OK));\r |
| 205 | break; /* done, clean up */\r |
| 206 | \r |
| 207 | case FNC_TRK: /* read track */\r |
| 208 | psec = dp_trkop (drv, sec); /* start of track */\r |
| 209 | for (cnt = 0; cnt < DP_NUMSC; cnt++) { /* full track */\r |
| 210 | if (r = dp_rdadr (uptr, psec, qnr, qwc)) /* read addr */\r |
| 211 | break; /* error? */\r |
| 212 | if (r = dp_rdsec (uptr, psec, qnr, qwc)) /* read data */\r |
| 213 | break; /* error? */\r |
| 214 | psec = dp_trkop (drv, sec) + ((psec + 1) % DP_NUMSC);\r |
| 215 | }\r |
| 216 | break; /* done, clean up */ \r |
| 217 | \r |
| 218 | case FNC_SEC + FNC_WRI: /* write */\r |
| 219 | if (cnt <= 0) return STOP_INVDCN; /* bad count? */\r |
| 220 | psec = dp_fndsec (uptr, sec, FALSE); /* find sector */\r |
| 221 | if (psec < 0) CRETIOE (dp_stop, STOP_DACERR); /* error? */\r |
| 222 | do { /* loop on count */\r |
| 223 | if (r = dp_tstgm (M[dp_ba], qnr)) break; /* start with gm? */\r |
| 224 | if (r = dp_wrsec (uptr, psec, qnr)) break; /* write data */\r |
| 225 | sec++; psec++; /* next sector */\r |
| 226 | } while ((--cnt > 0) &&\r |
| 227 | ((r = dp_nexsec (uptr, sec, psec, FALSE)) == SCPE_OK));\r |
| 228 | break; /* done, clean up */\r |
| 229 | \r |
| 230 | case FNC_TRK + FNC_WRI: /* write track */\r |
| 231 | if ((uptr->flags & UNIT_WAE) == 0) /* enabled? */\r |
| 232 | return STOP_WRADIS;\r |
| 233 | psec = dp_trkop (drv, sec); /* start of track */\r |
| 234 | for (cnt = 0; cnt < DP_NUMSC; cnt++) { /* full track */\r |
| 235 | if (r = dp_tstgm (M[dp_ba], qnr)) break; /* start with gm? */\r |
| 236 | if (r = dp_wradr (uptr, psec, qnr)) break; /* write addr */\r |
| 237 | if (r = dp_wrsec (uptr, psec, qnr)) break; /* write data */\r |
| 238 | psec = dp_trkop (drv, sec) + ((psec + 1) % DP_NUMSC);\r |
| 239 | }\r |
| 240 | break; /* done, clean up */\r |
| 241 | \r |
| 242 | default: /* unknown */\r |
| 243 | return STOP_INVFNC;\r |
| 244 | }\r |
| 245 | \r |
| 246 | if ((r == SCPE_OK) && !qnr) { /* eor check? */\r |
| 247 | if ((M[dp_ba] & DIGIT) != GRP_MARK) { /* GM at end? */\r |
| 248 | ind[IN_DWLR] = ind[IN_DERR] = 1; /* no, error */\r |
| 249 | r = STOP_WRLERR;\r |
| 250 | }\r |
| 251 | }\r |
| 252 | if ((r != SCPE_OK) && /* error? */\r |
| 253 | (dp_stop || !ind[IN_DERR])) return r; /* iochk or stop? */\r |
| 254 | return SCPE_OK; /* continue */\r |
| 255 | }\r |
| 256 | \r |
| 257 | /* Read or compare address with memory */\r |
| 258 | \r |
| 259 | t_stat dp_rdadr (UNIT *uptr, int32 sec, int32 qnr, int32 qwc)\r |
| 260 | {\r |
| 261 | int32 i;\r |
| 262 | uint8 ad;\r |
| 263 | int32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */\r |
| 264 | uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */\r |
| 265 | t_bool zad = dp_zeroad (ap); /* zero address */\r |
| 266 | static const int32 dec_tab[DP_ADDR] = { /* powers of 10 */\r |
| 267 | 10000, 1000, 100, 10, 1\r |
| 268 | } ;\r |
| 269 | \r |
| 270 | for (i = 0; i < DP_ADDR; i++) { /* copy/check addr */\r |
| 271 | if (zad) { /* addr zero? */\r |
| 272 | ad = sec / dec_tab[i]; /* get addr digit */\r |
| 273 | sec = sec % dec_tab[i]; /* get remainder */\r |
| 274 | }\r |
| 275 | else ad = *ap; /* addr digit */\r |
| 276 | if (qwc) { /* write check? */\r |
| 277 | if (dp_tstgm (M[dp_ba], qnr)) /* grp mrk in mem? */\r |
| 278 | return STOP_WRLERR; /* yes, error */\r |
| 279 | if (!zad && (M[dp_ba] != ad)) { /* digits equal? */\r |
| 280 | ind[IN_DACH] = ind[IN_DERR] = 1; /* no, error */\r |
| 281 | return STOP_DWCERR;\r |
| 282 | }\r |
| 283 | }\r |
| 284 | else M[dp_ba] = ad & (FLAG | DIGIT); /* store digit */\r |
| 285 | if (dp_tstgm (*ap, qnr)) return STOP_WRLERR; /* grp mrk on disk? */\r |
| 286 | ap++; PP (dp_ba); /* adv ptrs */\r |
| 287 | }\r |
| 288 | return SCPE_OK;\r |
| 289 | }\r |
| 290 | \r |
| 291 | /* Read or compare data with memory */\r |
| 292 | \r |
| 293 | t_stat dp_rdsec (UNIT *uptr, int32 sec, int32 qnr, int32 qwc)\r |
| 294 | {\r |
| 295 | int32 i;\r |
| 296 | int32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */\r |
| 297 | uint8 *ap = ((uint8 *) uptr->filebuf) + da + DP_ADDR; /* buf ptr */\r |
| 298 | \r |
| 299 | for (i = 0; i < DP_DATA; i++) { /* copy data */\r |
| 300 | if (qwc) { /* write check? */\r |
| 301 | if (dp_tstgm (M[dp_ba], qnr)) /* grp mrk in mem? */\r |
| 302 | return STOP_WRLERR; /* yes, error */\r |
| 303 | if (M[dp_ba] != *ap) { /* dig+flags equal? */\r |
| 304 | ind[IN_DACH] = ind[IN_DERR] = 1; /* no, error */\r |
| 305 | return STOP_DWCERR;\r |
| 306 | }\r |
| 307 | }\r |
| 308 | else M[dp_ba] = *ap & (FLAG | DIGIT); /* flag + digit */\r |
| 309 | if (dp_tstgm (*ap, qnr)) return STOP_WRLERR; /* grp mrk on disk? */\r |
| 310 | ap++; PP (dp_ba); /* adv ptrs */\r |
| 311 | }\r |
| 312 | return SCPE_OK;\r |
| 313 | }\r |
| 314 | \r |
| 315 | /* Write address to disk */\r |
| 316 | \r |
| 317 | t_stat dp_wradr (UNIT *uptr, int32 sec, int32 qnr)\r |
| 318 | {\r |
| 319 | int32 i;\r |
| 320 | uint32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */\r |
| 321 | uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */\r |
| 322 | \r |
| 323 | for (i = 0; i < DP_ADDR; i++) { /* copy address */\r |
| 324 | *ap = M[dp_ba] & (FLAG | DIGIT); /* flag + digit */\r |
| 325 | if (da >= uptr->hwmark) uptr->hwmark = da + 1;\r |
| 326 | if (dp_tstgm (*ap, qnr)) { /* grp mrk fm mem? */\r |
| 327 | dp_fill (uptr, da + 1, DP_NUMCH - i - 1); /* fill addr+data */\r |
| 328 | return STOP_WRLERR; /* error */\r |
| 329 | }\r |
| 330 | da++; ap++; PP (dp_ba); /* adv ptrs */\r |
| 331 | }\r |
| 332 | return SCPE_OK;\r |
| 333 | }\r |
| 334 | \r |
| 335 | /* Write data to disk */\r |
| 336 | \r |
| 337 | t_stat dp_wrsec (UNIT *uptr, int32 sec, int32 qnr)\r |
| 338 | {\r |
| 339 | int32 i;\r |
| 340 | uint32 da = ((sec % DP_TOTSC) * DP_NUMCH) + DP_ADDR; /* char number */\r |
| 341 | uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */\r |
| 342 | \r |
| 343 | for (i = 0; i < DP_DATA; i++) { /* copy data */\r |
| 344 | *ap = M[dp_ba] & (FLAG | DIGIT); /* get character */\r |
| 345 | if (da >= uptr->hwmark) uptr->hwmark = da + 1;\r |
| 346 | if (dp_tstgm (*ap, qnr)) { /* grp mrk fm mem? */\r |
| 347 | dp_fill (uptr, da + 1, DP_DATA - i - 1); /* fill data */ \r |
| 348 | return STOP_WRLERR; /* error */\r |
| 349 | }\r |
| 350 | da++; ap++; PP (dp_ba); /* adv ptrs */\r |
| 351 | }\r |
| 352 | return SCPE_OK;\r |
| 353 | }\r |
| 354 | \r |
| 355 | /* Find sector */\r |
| 356 | \r |
| 357 | int32 dp_fndsec (UNIT *uptr, int32 sec, t_bool rd)\r |
| 358 | {\r |
| 359 | int32 ctrk = sec % (DP_NUMSF * DP_NUMSC); /* curr trk-sec */\r |
| 360 | int32 psec = ((uptr->CYL) * (DP_NUMSF * DP_NUMSC)) + ctrk;\r |
| 361 | int32 da = psec * DP_NUMCH; /* char number */\r |
| 362 | uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */\r |
| 363 | int32 dskad, i;\r |
| 364 | \r |
| 365 | if (dp_zeroad (ap)) return psec; /* addr zero? ok */\r |
| 366 | dskad = dp_cvt_ad (ap); /* cvt addr */\r |
| 367 | if (dskad == sec) { /* match? */\r |
| 368 | if (rd || ((*ap & FLAG) == 0)) return psec; /* read or !wprot? */\r |
| 369 | ind[IN_DACH] = ind[IN_DERR] = 1; /* no match */\r |
| 370 | return -1;\r |
| 371 | } \r |
| 372 | psec = psec - (psec % DP_NUMSC); /* sector 0 */\r |
| 373 | for (i = 0; i < DP_NUMSC; i++, psec++) { /* check track */\r |
| 374 | da = psec * DP_NUMCH; /* char number */\r |
| 375 | ap = ((uint8 *) uptr->filebuf) + da; /* word pointer */\r |
| 376 | if (dp_zeroad (ap)) continue; /* no implicit match */\r |
| 377 | dskad = dp_cvt_ad (ap); /* cvt addr */\r |
| 378 | if (dskad == sec) { /* match? */\r |
| 379 | if (rd || ((*ap & FLAG) == 0)) return psec; /* read or !wprot? */\r |
| 380 | ind[IN_DACH] = ind[IN_DERR] = 1; /* no match */\r |
| 381 | return -1;\r |
| 382 | }\r |
| 383 | } \r |
| 384 | ind[IN_DACH] = ind[IN_DERR] = 1; /* no match */\r |
| 385 | return -1;\r |
| 386 | }\r |
| 387 | \r |
| 388 | /* Find next sector - must be sequential, cannot cross cylinder boundary */\r |
| 389 | \r |
| 390 | t_stat dp_nexsec (UNIT *uptr, int32 sec, int32 psec, t_bool rd)\r |
| 391 | {\r |
| 392 | int32 ctrk = psec % (DP_NUMSF * DP_NUMSC); /* curr trk-sec */\r |
| 393 | int32 da = psec * DP_NUMCH; /* word number */\r |
| 394 | uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */\r |
| 395 | int32 dskad;\r |
| 396 | \r |
| 397 | if (ctrk) { /* not trk zero? */\r |
| 398 | if (dp_zeroad (ap)) return SCPE_OK; /* addr zero? ok */\r |
| 399 | dskad = dp_cvt_ad (ap); /* cvt addr */\r |
| 400 | if ((dskad == sec) && /* match? */\r |
| 401 | (rd || ((*ap & FLAG) == 0))) return SCPE_OK; /* read or !wprot? */\r |
| 402 | ind[IN_DACH] = ind[IN_DERR] = 1; /* no, error */\r |
| 403 | return STOP_DACERR;\r |
| 404 | }\r |
| 405 | ind[IN_DCYO] = ind[IN_DERR] = 1; /* cyl overflow */\r |
| 406 | return STOP_CYOERR;\r |
| 407 | }\r |
| 408 | \r |
| 409 | /* Test for zero address */\r |
| 410 | \r |
| 411 | t_bool dp_zeroad (uint8 *ap)\r |
| 412 | {\r |
| 413 | int32 i;\r |
| 414 | \r |
| 415 | for (i = 0; i < DP_ADDR; i++, ap++) { /* loop thru addr */\r |
| 416 | if (*ap & DIGIT) return FALSE; /* nonzero? lose */\r |
| 417 | }\r |
| 418 | return TRUE; /* all zeroes */\r |
| 419 | }\r |
| 420 | \r |
| 421 | /* Test for group mark when enabled */\r |
| 422 | \r |
| 423 | t_stat dp_tstgm (uint32 c, int32 qnr)\r |
| 424 | {\r |
| 425 | if (!qnr && ((c & DIGIT) == GRP_MARK)) { /* premature GM? */\r |
| 426 | ind[IN_DWLR] = ind[IN_DERR] = 1; /* error */\r |
| 427 | return STOP_WRLERR;\r |
| 428 | }\r |
| 429 | return SCPE_OK;\r |
| 430 | }\r |
| 431 | \r |
| 432 | /* Convert disk address to binary - invalid char force bad address */\r |
| 433 | \r |
| 434 | int32 dp_cvt_ad (uint8 *ap)\r |
| 435 | {\r |
| 436 | int32 i, r;\r |
| 437 | uint8 c;\r |
| 438 | \r |
| 439 | for (i = r = 0; i < DP_ADDR; i++, ap++) { /* loop thru addr */\r |
| 440 | c = *ap & DIGIT; /* get digit */\r |
| 441 | if (BAD_DIGIT (c)) return -1; /* bad digit? */\r |
| 442 | r = (r * 10) + c; /* bcd to binary */\r |
| 443 | }\r |
| 444 | return r;\r |
| 445 | }\r |
| 446 | \r |
| 447 | /* Track operation setup */\r |
| 448 | \r |
| 449 | int32 dp_trkop (int32 drv, int32 sec)\r |
| 450 | {\r |
| 451 | int32 ctrk = (sec / DP_NUMSC) % DP_NUMSF;\r |
| 452 | \r |
| 453 | return ((drv * DP_TOTSC) + (dp_unit[drv].CYL * DP_NUMSF * DP_NUMSC) +\r |
| 454 | (ctrk * DP_NUMSC));\r |
| 455 | }\r |
| 456 | \r |
| 457 | /* Convert DCF BCD field to binary */\r |
| 458 | \r |
| 459 | int32 dp_cvt_bcd (uint32 ad, int32 len)\r |
| 460 | {\r |
| 461 | uint8 c;\r |
| 462 | int32 r;\r |
| 463 | \r |
| 464 | for (r = 0; len > 0; len--) { /* loop thru char */\r |
| 465 | c = M[ad] & DIGIT; /* get digit */\r |
| 466 | if (BAD_DIGIT (c)) return -1; /* invalid? */\r |
| 467 | r = (r * 10) + c; /* cvt to bin */\r |
| 468 | PP (ad); /* next digit */\r |
| 469 | }\r |
| 470 | return r;\r |
| 471 | }\r |
| 472 | \r |
| 473 | /* Fill sector buffer with zero */\r |
| 474 | \r |
| 475 | void dp_fill (UNIT *uptr, uint32 da, int32 cnt)\r |
| 476 | {\r |
| 477 | while (cnt-- > 0) { /* fill with zeroes*/\r |
| 478 | *(((uint8 *) uptr->filebuf) + da) = 0;\r |
| 479 | if (da >= uptr->hwmark) uptr->hwmark = da + 1;\r |
| 480 | da++;\r |
| 481 | }\r |
| 482 | return;\r |
| 483 | }\r |
| 484 | \r |
| 485 | /* Reset routine */\r |
| 486 | \r |
| 487 | t_stat dp_reset (DEVICE *dptr)\r |
| 488 | {\r |
| 489 | int32 i;\r |
| 490 | \r |
| 491 | for (i = 0; i < DP_NUMDR; i++) dp_unit[i].CYL = 0; /* reset cylinder */\r |
| 492 | ind[IN_DACH] = ind[IN_DWLR] = 0; /* clr indicators */\r |
| 493 | ind[IN_DERR] = ind[IN_DCYO] = 0;\r |
| 494 | return SCPE_OK;\r |
| 495 | }\r |