1 /* i1401_dp.c: IBM 1311 disk simulator
2
3 Copyright (c) 2002-2008, Robert M. Supnik
4
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 Except as contained in this notice, the name of Robert M Supnik shall not be
23 used in advertising or otherwise to promote the sale, use or other dealings
24 in this Software without prior written authorization from Robert M Supnik.
25
26 dp 1311 disk pack
27
28 18-Oct-02 RMS Fixed bug in address comparison logic
29 19-Sep-02 RMS Minor edit for consistency with 1620
30 15-Jun-02 RMS Reworked address comparison logic
31
32 The 1311 disk pack has 100 cylinders, 10 tracks/cylinder, 20 sectors/track.
33 Each sector contains 106 characters of information:
34
35 6c sector address
36 100c sector data
37
38 By default, a sector's address field will be '000000', which is illegal.
39 This is interpreted to mean the implied sector number that would be in
40 place if the disk pack had been formatted with sequential sector numbers.
41
42 The sector data can be 100 characters without word marks, or 90 characters
43 with word marks. Load mode transfers 90 characters per sector with
44 word marks, move mode transfers 100 characters per sector without word
45 marks. No attempt is made to catch incompatible writes (eg, load mode
46 write followed by move mode read).
47 */
48
49 #include "i1401_defs.h"
50
51 #define DP_NUMDR 5 /* #drives */
52 #define UNIT_V_WAE (UNIT_V_UF + 0) /* write addr enab */
53 #define UNIT_WAE (1 << UNIT_V_WAE)
54
55 /* Disk format */
56
57 #define DP_ADDR 6 /* address */
58 #define DP_DATA 100 /* data */
59 #define DP_NUMCH (DP_ADDR + DP_DATA)
60
61 #define DP_NUMSC 20 /* #sectors */
62 #define DP_NUMSF 10 /* #surfaces */
63 #define DP_NUMCY 100 /* #cylinders */
64 #define DP_TOTSC (DP_NUMCY*DP_NUMSF*DP_NUMSC)
65 #define DP_SIZE (DP_TOTSC*DP_NUMCH)
66
67 /* Disk control field */
68
69 #define DCF_DRV 0 /* drive select */
70 #define DCF_SEC 1 /* sector addr */
71 #define DCF_SEC_LEN 6
72 #define DCF_CNT (DCF_SEC + DCF_SEC_LEN) /* sector count */
73 #define DCF_CNT_LEN 3
74 #define DCF_LEN (DCF_CNT + DCF_CNT_LEN)
75 #define DCF_DIR 1 /* direct seek */
76 #define DCF_DIR_LEN 4
77 #define DCF_DIR_FL (DCF_DIR + DCF_DIR_LEN) /* direct seek flag */
78 #define DCF_DSEEK 0xB
79
80 /* Functions */
81
82 #define FNC_SEEK 0 /* seek */
83 #define FNC_CHECK 3 /* check */
84 #define FNC_READ 1 /* read sectors */
85 #define FNC_RSCO 5 /* read sec cnt overlay */
86 #define FNC_RTRK 6 /* read track */
87 #define FNC_WOFF 10 /* offset for write */
88 #define FNC_WRITE 11 /* write sectors */
89 #define FNC_WRSCO 15 /* write sec cnt overlay */
90 #define FNC_WRTRK 16 /* write track */
91
92 #define CYL u3 /* current cylinder */
93
94 extern uint8 M[]; /* memory */
95 extern int32 ind[64];
96 extern int32 AS, BS, iochk;
97 extern int32 bcd_to_bin[16];
98 extern int32 bin_to_bcd[16];
99 extern UNIT cpu_unit;
100
101 int32 dp_lastf = 0; /* prior function */
102 int32 dp_time = 0; /* seek time */
103
104 t_stat dp_reset (DEVICE *dptr);
105 t_stat dp_rdadr (UNIT *uptr, int32 sec, int32 flg, int32 wchk);
106 t_stat dp_rdsec (UNIT *uptr, int32 sec, int32 flg, int32 wchk);
107 t_stat dp_wradr (UNIT *uptr, int32 sec, int32 flg);
108 t_stat dp_wrsec (UNIT *uptr, int32 sec, int32 flg);
109 int32 dp_fndsec (UNIT *uptr, int32 sec, int32 dcf);
110 t_stat dp_nexsec (UNIT *uptr, int32 psec, int32 dcf);
111 t_bool dp_zeroad (uint8 *ap);
112 t_bool dp_cmp_ad (uint8 *ap, int32 dcf);
113 int32 dp_trkop (int32 drv, int32 sec);
114 int32 dp_cvt_bcd (int32 ad, int32 len);
115 void dp_cvt_bin (int32 ad, int32 len, int32 val, int32 flg);
116 int32 dp_get_cnt (int32 dcf);
117 void dp_fill (UNIT *uptr, uint32 da, int32 cnt);
118
119 /* DP data structures
120
121 dp_dev DSK device descriptor
122 dp_unit DSK unit list
123 dp_reg DSK register list
124 dp_mod DSK modifier list
125 */
126
127 UNIT dp_unit[] = {
128 { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE +
129 UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) },
130 { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE +
131 UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) },
132 { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE +
133 UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) },
134 { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE +
135 UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) },
136 { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE +
137 UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) }
138 };
139
140 REG dp_reg[] = {
141 { FLDATA (ACC, ind[IN_ACC], 0) },
142 { FLDATA (PWC, ind[IN_DPW], 0) },
143 { FLDATA (WLR, ind[IN_LNG], 0) },
144 { FLDATA (UNA, ind[IN_UNA], 0) },
145 { FLDATA (ERR, ind[IN_DSK], 0) },
146 { FLDATA (BSY, ind[IN_DBY], 0) },
147 { DRDATA (LASTF, dp_lastf, 3) },
148 { DRDATA (TIME, dp_time, 24), PV_LEFT },
149 { URDATA (CYL, dp_unit[0].CYL, 10, 8, 0,
150 DP_NUMDR, PV_LEFT + REG_RO) },
151 { NULL }
152 };
153
154 MTAB dp_mod[] = {
155 { UNIT_WAE, 0, "write address disabled", "ADDROFF", NULL },
156 { UNIT_WAE, UNIT_WAE, "write address enabled", "ADDRON", NULL },
157 { 0 }
158 };
159
160 DEVICE dp_dev = {
161 "DP", dp_unit, dp_reg, dp_mod,
162 DP_NUMDR, 10, 21, 1, 8, 7,
163 NULL, NULL, &dp_reset,
164 NULL, NULL, NULL
165 };
166
167 /* Disk IO routine
168
169 Inputs:
170 fnc = function character
171 flg = load vs move mode
172 mod = modifier character
173 Outputs:
174 status = status
175 */
176
dp_io(int32 fnc,int32 flg,int32 mod)177 t_stat dp_io (int32 fnc, int32 flg, int32 mod)
178 {
179 int32 dcf, drv, sec, psec, cnt, qwc, qzr, diff;
180 UNIT *uptr;
181 t_stat r;
182
183 dcf = BS; /* save DCF addr */
184 qwc = 0; /* not wcheck */
185 ind[IN_DPW] = ind[IN_LNG] = ind[IN_UNA] = 0; /* clr indicators */
186 ind[IN_DSK] = ind[IN_ACC] = ind[IN_DBY] = 0;
187 if (sim_is_active (&dp_unit[0])) { /* ctlr busy? */
188 ind[IN_DBY] = ind[IN_DSK] = 1; /* set indicators */
189 return SCPE_OK; /* done */
190 }
191
192 AS = dcf + 6; /* AS for most ops */
193 BS = dcf + DCF_CNT - 1; /* minimum DCF */
194 if (ADDR_ERR (BS)) /* DCF in memory? */
195 return STOP_WRAP;
196 if (M[dcf] & BBIT) /* impl sel? cyl 8-4-2 */
197 drv = M[dcf + DCF_SEC + 1] & 0xE;
198 else drv = M[dcf] & DIGIT; /* get drive sel */
199 if ((drv == 0) || (drv & 1) || (drv > BCD_ZERO)) /* bad drive #? */
200 return STOP_INVDSK;
201 drv = bcd_to_bin[drv] >> 1; /* convert */
202 uptr = dp_dev.units + drv; /* get unit ptr */
203 if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */
204 ind[IN_DSK] = ind[IN_ACC] = 1; /* no, error */
205 CRETIOE (iochk, SCPE_UNATT);
206 }
207
208 if ((fnc == FNC_SEEK) && /* seek and */
209 (M[dcf + DCF_DIR_FL] & DCF_DSEEK) == DCF_DSEEK) { /* direct flag? */
210 diff = dp_cvt_bcd (dcf + DCF_DIR, DCF_DIR_LEN); /* cvt diff */
211 if (diff < 0) /* error? */
212 return STOP_INVDSC;
213 diff = diff >> 1; /* diff is *2 */
214 if ((M[dcf + DCF_DIR + DCF_DIR_LEN - 1] & ZONE) == BBIT)
215 diff = -diff; /* get sign */
216 uptr->CYL = uptr->CYL + diff; /* bound seek */
217 if (uptr->CYL < 0)
218 uptr->CYL = 0;
219 else if (uptr->CYL >= DP_NUMCY) { /* too big? */
220 uptr->CYL = 0; /* system hangs */
221 return STOP_INVDCY;
222 }
223 sim_activate (&dp_unit[0], dp_time); /* set ctlr busy */
224 return SCPE_OK; /* done! */
225 }
226
227 sec = dp_cvt_bcd (dcf + DCF_SEC, DCF_SEC_LEN); /* cvt sector */
228 if ((sec < 0) || (sec >= (DP_NUMDR * DP_TOTSC))) /* bad sector? */
229 return STOP_INVDSC;
230 if (fnc == FNC_SEEK) { /* seek? */
231 uptr->CYL = (sec / (DP_NUMSF * DP_NUMSC)) % /* set cyl # */
232 DP_NUMCY;
233 sim_activate (&dp_unit[0], dp_time); /* set ctlr busy */
234 return SCPE_OK; /* done! */
235 }
236
237 BS = dcf + DCF_LEN; /* full DCF */
238 if (ADDR_ERR (BS)) /* DCF in memory? */
239 return STOP_WRAP;
240 cnt = dp_get_cnt (dcf); /* get count */
241 if (cnt < 0) /* bad count? */
242 return STOP_INVDCN;
243
244 if (fnc >= FNC_WOFF) /* invalid func */
245 return STOP_INVDFN;
246 if (mod == BCD_W) { /* write? */
247 if (fnc == FNC_CHECK) { /* write check? */
248 qwc = 1; /* special read */
249 fnc = dp_lastf; /* use last func */
250 }
251 else {
252 dp_lastf = fnc; /* save func */
253 fnc = fnc + FNC_WOFF; /* change to write */
254 }
255 }
256 else if (mod == BCD_R) /* read? save func */
257 dp_lastf = fnc;
258 else return STOP_INVM; /* other? error */
259
260 switch (fnc) { /* case on function */
261
262 case FNC_RSCO: /* read sec cnt ov */
263 BS = dcf + DCF_CNT; /* set count back */
264 /* fall thru */
265 case FNC_READ: /* read */
266 psec = dp_fndsec (uptr, sec, dcf); /* find sector */
267 if (psec < 0) /* addr cmp error? */
268 CRETIOE (iochk, STOP_INVDAD);
269 for (;;) { /* loop */
270 qzr = (--cnt == 0); /* set zero latch */
271 dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* redo count */
272 if ((r = dp_rdsec (uptr, psec, flg, qwc))) /* read sector */
273 break;
274 cnt = dp_get_cnt (dcf); /* get new count */
275 if (cnt < 0) /* bad count? */
276 return STOP_INVDCN;
277 if (qzr) /* zero latch? done */
278 break;
279 sec++; psec++; /* next sector */
280 dp_cvt_bin (dcf + DCF_SEC, DCF_SEC_LEN, sec, flg); /* rewr sec */
281 if ((r = dp_nexsec (uptr, psec, dcf))) /* find next */
282 break;
283 }
284 break; /* done, clean up */
285
286 case FNC_RTRK: /* read track */
287 AS = dcf + 9; /* special AS */
288 psec = dp_trkop (drv, sec); /* start of track */
289 for (;;) { /* loop */
290 qzr = (--cnt == 0); /* set zero latch */
291 dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* redo count */
292 if ((r = dp_rdadr (uptr, psec, flg, qwc))) /* read addr */
293 break; /* error? */
294 if ((r = dp_rdsec (uptr, psec, flg, qwc))) /* read data */
295 break; /* error? */
296 cnt = dp_get_cnt (dcf); /* get new count */
297 if (cnt < 0) /* bad count? */
298 return STOP_INVDCN;
299 if (qzr) /* zero latch? done */
300 break;
301 psec = dp_trkop (drv, sec) + ((psec + 1) % DP_NUMSC);
302 }
303 break; /* done, clean up */
304
305 case FNC_WRSCO: /* write sec cnt ov */
306 BS = dcf + DCF_CNT; /* set count back */
307 /* fall through */
308 case FNC_WRITE: /* read */
309 psec = dp_fndsec (uptr, sec, dcf); /* find sector */
310 if (psec < 0) /* addr cmp error? */
311 CRETIOE (iochk, STOP_INVDAD);
312 for (;;) { /* loop */
313 qzr = (--cnt == 0); /* set zero latch */
314 dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* rewr cnt */
315 if ((r = dp_wrsec (uptr, psec, flg))) /* write data */
316 break;
317 if (qzr) /* zero latch? done */
318 break;
319 sec++; psec++; /* next sector */
320 dp_cvt_bin (dcf + DCF_SEC, DCF_SEC_LEN, sec, flg); /* rewr sec */
321 if ((r = dp_nexsec (uptr, psec, dcf))) /* find next */
322 break;
323 }
324 break; /* done, clean up */
325
326 case FNC_WRTRK: /* write track */
327 if ((uptr->flags & UNIT_WAE) == 0) /* enabled? */
328 return STOP_WRADIS;
329 AS = dcf + 9; /* special AS */
330 psec = dp_trkop (drv, sec); /* start of track */
331 for (;;) { /* loop */
332 qzr = (--cnt == 0); /* set zero latch */
333 dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* redo count */
334 if ((r = dp_wradr (uptr, psec, flg))) /* write addr */
335 break;
336 if ((r = dp_wrsec (uptr, psec, flg))) /* write data */
337 break;
338 if (qzr) /* zero latch? done */
339 break;
340 psec = dp_trkop (drv, sec) + ((psec + 1) % DP_NUMSC);
341 }
342 break; /* done, clean up */
343
344 default: /* unknown */
345 return STOP_INVDFN;
346 }
347
348 if (r == SCPE_OK) { /* normal so far? */
349 BS++; /* advance BS */
350 if (ADDR_ERR (BS)) /* address error? */
351 return STOP_WRAP;
352 if (M[BS - 1] != (WM + BCD_GRPMRK)) { /* GM + WM at end? */
353 ind[IN_LNG] = ind[IN_DSK] = 1; /* no, error */
354 r = STOP_INVDLN;
355 }
356 }
357 CRETIOE (iochk || !ind[IN_DSK], r); /* return status */
358 }
359
360 /* Read or compare address with memory */
361
dp_rdadr(UNIT * uptr,int32 sec,int32 flg,int32 qwc)362 t_stat dp_rdadr (UNIT *uptr, int32 sec, int32 flg, int32 qwc)
363 {
364 int32 i;
365 uint8 ac;
366 int32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */
367 uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */
368 t_bool zad = dp_zeroad (ap); /* zero address */
369 static const int32 dec_tab[DP_ADDR] = { /* powers of 10 */
370 100000, 10000, 1000, 100, 10, 1
371 } ;
372
373 for (i = 0; i < DP_ADDR; i++) { /* copy address */
374 if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */
375 ind[IN_LNG] = ind[IN_DSK] = 1; /* error */
376 return STOP_INVDLN;
377 }
378 if (zad) { /* addr zero? */
379 ac = sec / dec_tab[i]; /* get addr digit */
380 sec = sec % dec_tab[i]; /* get remainder */
381 ac = bcd_to_bin[ac]; /* cvt to BCD */
382 }
383 else ac = *ap; /* addr char */
384 if (qwc) { /* wr chk? skip if zad */
385 if (!zad && (flg? (M[BS] != ac): /* L? cmp with WM */
386 ((M[BS] & CHAR) != (ac & CHAR)))) { /* M? cmp w/o WM */
387 ind[IN_DPW] = ind[IN_DSK] = 1;
388 return STOP_WRCHKE;
389 }
390 }
391 else if (flg) /* load mode */
392 M[BS] = ac & CHAR;
393 else M[BS] = (M[BS] & WM) | (ac & CHAR); /* move mode */
394 ap++; BS++; /* adv ptrs */
395 if (ADDR_ERR (BS))
396 return STOP_WRAP;
397 }
398 return SCPE_OK;
399 }
400
401 /* Read or compare data with memory */
402
dp_rdsec(UNIT * uptr,int32 sec,int32 flg,int32 qwc)403 t_stat dp_rdsec (UNIT *uptr, int32 sec, int32 flg, int32 qwc)
404 {
405 int32 i, lim;
406 int32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */
407 uint8 *ap = ((uint8 *) uptr->filebuf) + da + DP_ADDR; /* buf ptr */
408
409 lim = flg? (DP_DATA - 10): DP_DATA; /* load vs move */
410 for (i = 0; i < lim; i++) { /* copy data */
411 if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */
412 ind[IN_LNG] = ind[IN_DSK] = 1; /* error */
413 return STOP_INVDLN;
414 }
415 if (qwc) { /* write check? */
416 if (flg? (M[BS] != *ap): /* load mode cmp */
417 ((M[BS] & CHAR) != (*ap & CHAR))) { /* move mode cmp */
418 ind[IN_DPW] = ind[IN_DSK] = 1; /* error */
419 return STOP_WRCHKE;
420 }
421 }
422 else if (flg) /* load mode */
423 M[BS] = *ap & (WM | CHAR);
424 else M[BS] = (M[BS] & WM) | (*ap & CHAR); /* word mode */
425 ap++; /* adv ptrs */
426 BS++;
427 if (ADDR_ERR (BS))
428 return STOP_WRAP;
429 }
430 return SCPE_OK;
431 }
432
433 /* Write address to disk */
434
dp_wradr(UNIT * uptr,int32 sec,int32 flg)435 t_stat dp_wradr (UNIT *uptr, int32 sec, int32 flg)
436 {
437 int32 i;
438 uint32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */
439 uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */
440
441 for (i = 0; i < DP_ADDR; i++) { /* copy address */
442 if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */
443 dp_fill (uptr, da, DP_NUMCH - i); /* fill, set err */
444 ind[IN_LNG] = ind[IN_DSK] = 1; /* error */
445 return STOP_INVDLN;
446 }
447 if (flg) /* L? copy WM */
448 *ap = M[BS] & (WM | CHAR);
449 else *ap = M[BS] & CHAR; /* M? strip WM */
450 if (da >= uptr->hwmark)
451 uptr->hwmark = da + 1;
452 da++; /* adv ptrs */
453 ap++;
454 BS++;
455 if (ADDR_ERR (BS))
456 return STOP_WRAP;
457 }
458 return SCPE_OK;
459 }
460
461 /* Write data to disk */
462
dp_wrsec(UNIT * uptr,int32 sec,int32 flg)463 t_stat dp_wrsec (UNIT *uptr, int32 sec, int32 flg)
464 {
465 int32 i, lim;
466 uint32 da = ((sec % DP_TOTSC) * DP_NUMCH) + DP_ADDR; /* char number */
467 uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */
468
469 lim = flg? (DP_DATA - 10): DP_DATA; /* load vs move */
470 for (i = 0; i < lim; i++) { /* copy data */
471 if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */
472 dp_fill (uptr, da, DP_DATA - i); /* fill, set err */
473 ind[IN_LNG] = ind[IN_DSK] = 1; /* error */
474 return STOP_INVDLN;
475 }
476 if (flg) /* load, copy WM */
477 *ap = M[BS] & (WM | CHAR);
478 else *ap = M[BS] & CHAR; /* move, strip WM */
479 if (da >= uptr->hwmark)
480 uptr->hwmark = da + 1;
481 da++; /* adv ptrs */
482 ap++;
483 BS++;
484 if (ADDR_ERR (BS))
485 return STOP_WRAP;
486 }
487 return SCPE_OK;
488 }
489
490 /* Find sector */
491
dp_fndsec(UNIT * uptr,int32 sec,int32 dcf)492 int32 dp_fndsec (UNIT *uptr, int32 sec, int32 dcf)
493 {
494 int32 ctrk = sec % (DP_NUMSF * DP_NUMSC); /* curr trk-sec */
495 int32 psec = ((uptr->CYL) * (DP_NUMSF * DP_NUMSC)) + ctrk;
496 int32 da = psec * DP_NUMCH; /* char number */
497 uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */
498 int32 i;
499
500 if (dp_zeroad (ap)) /* addr zero? ok */
501 return psec;
502 if (dp_cmp_ad (ap, dcf)) /* addr comp? ok */
503 return psec;
504 psec = psec - (psec % DP_NUMSC); /* sector 0 */
505 for (i = 0; i < DP_NUMSC; i++, psec++) { /* check track */
506 da = psec * DP_NUMCH; /* char number */
507 ap = ((uint8 *) uptr->filebuf) + da; /* word pointer */
508 if (dp_zeroad (ap)) /* no implicit match */
509 continue;
510 if (dp_cmp_ad (ap, dcf)) /* match? */
511 return psec;
512 }
513 ind[IN_UNA] = ind[IN_DSK] = 1; /* no match */
514 return -1;
515 }
516
517 /* Find next sector - must be sequential, cannot cross cylinder boundary */
518
dp_nexsec(UNIT * uptr,int32 psec,int32 dcf)519 t_stat dp_nexsec (UNIT *uptr, int32 psec, int32 dcf)
520 {
521 int32 ctrk = psec % (DP_NUMSF * DP_NUMSC); /* curr trk-sec */
522 int32 da = psec * DP_NUMCH; /* word number */
523 uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */
524
525 if (ctrk) { /* not trk zero? */
526 if (dp_zeroad (ap)) /* addr zero? ok */
527 return SCPE_OK;
528 if (dp_cmp_ad (ap, dcf)) /* addr comp? ok */
529 return SCPE_OK;
530 }
531 ind[IN_UNA] = ind[IN_DSK] = 1; /* no, error */
532 return STOP_INVDAD;
533 }
534
535 /* Test for zero address */
536
dp_zeroad(uint8 * ap)537 t_bool dp_zeroad (uint8 *ap)
538 {
539 int32 i;
540
541 for (i = 0; i < DP_ADDR; i++, ap++) { /* loop thru addr */
542 if (*ap & CHAR) /* nonzero? lose */
543 return FALSE;
544 }
545 return TRUE; /* all zeroes */
546 }
547
548 /* Compare disk address to memory sector address - always omit word marks */
549
dp_cmp_ad(uint8 * ap,int32 dcf)550 t_bool dp_cmp_ad (uint8 *ap, int32 dcf)
551 {
552 int32 i;
553 uint8 c;
554
555 for (i = 0; i < DP_ADDR; i++, ap++) { /* loop thru addr */
556 c = M[dcf + DCF_SEC + i]; /* sector addr char */
557 if ((c & CHAR) != (*ap & CHAR)) /* cmp w/o WM */
558 return FALSE;
559 }
560 return TRUE; /* compare ok */
561 }
562
563 /* Track operation setup */
564
dp_trkop(int32 drv,int32 sec)565 int32 dp_trkop (int32 drv, int32 sec)
566 {
567 int32 ctrk = (sec / DP_NUMSC) % DP_NUMSF;
568
569 return ((drv * DP_TOTSC) + (dp_unit[drv].CYL * DP_NUMSF * DP_NUMSC) +
570 (ctrk * DP_NUMSC));
571 }
572
573 /* Convert DCF BCD field to binary */
574
dp_cvt_bcd(int32 ad,int32 len)575 int32 dp_cvt_bcd (int32 ad, int32 len)
576 {
577 uint8 c;
578 int32 r;
579
580 for (r = 0; len > 0; len--) { /* loop thru char */
581 c = M[ad] & DIGIT; /* get digit */
582 if ((c == 0) || (c > BCD_ZERO)) /* invalid? */
583 return -1;
584 r = (r * 10) + bcd_to_bin[c]; /* cvt to bin */
585 ad++; /* next digit */
586 }
587 return r;
588 }
589
590 /* Convert binary to DCF BCD field */
591
dp_cvt_bin(int32 ad,int32 len,int32 val,int32 flg)592 void dp_cvt_bin (int32 ad, int32 len, int32 val, int32 flg)
593 {
594 int32 r;
595
596 for ( ; len > 0; len--) { /* loop thru char */
597 r = val % 10; /* get digit */
598 if (flg) /* load mode? */
599 M[ad + len - 1] = bin_to_bcd[r];
600 else M[ad + len - 1] = (M[ad + len - 1] & WM) | bin_to_bcd[r];
601 val = val / 10;
602 }
603 return;
604 }
605
606 /* Get and validate count */
607
dp_get_cnt(int32 dcf)608 int32 dp_get_cnt (int32 dcf)
609 {
610 int32 cnt = dp_cvt_bcd (dcf + DCF_CNT, DCF_CNT_LEN); /* get new count */
611 if (cnt < 0) /* bad count? */
612 return -1;
613 if (cnt == 0) /* 0 => 1000 */
614 return 1000;
615 return cnt;
616 }
617
618 /* Fill sector buffer with blanks */
619
dp_fill(UNIT * uptr,uint32 da,int32 cnt)620 void dp_fill (UNIT *uptr, uint32 da, int32 cnt)
621 {
622 while (cnt-- > 0) { /* fill with blanks */
623 *(((uint8 *) uptr->filebuf) + da) = BCD_BLANK;
624 if (da >= uptr->hwmark)
625 uptr->hwmark = da + 1;
626 da++;
627 }
628 return;
629 }
630
631 /* Reset routine */
632
dp_reset(DEVICE * dptr)633 t_stat dp_reset (DEVICE *dptr)
634 {
635 int32 i;
636
637 for (i = 0; i < DP_NUMDR; i++) /* reset cylinder */
638 dp_unit[i].CYL = 0;
639 dp_lastf = 0; /* clear state */
640 ind[IN_DPW] = ind[IN_LNG] = ind[IN_UNA] = 0; /* clr indicators */
641 ind[IN_DSK] = ind[IN_ACC] = ind[IN_DBY] = 0;
642 sim_cancel (&dp_unit[0]); /* cancel timer */
643 return SCPE_OK;
644 }
645