First Commit of my working state
[simh.git] / I1401 / i1401_dp.c
1 /* i1401_dp.c: IBM 1311 disk simulator
2
3 Copyright (c) 2002-2005, 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
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)) return STOP_WRAP; /* DCF in memory? */
195 if (M[dcf] & BBIT) drv = M[dcf + DCF_SEC + 1] & 0xE; /* impl sel? cyl 8-4-2 */
196 else drv = M[dcf] & DIGIT; /* get drive sel */
197 if ((drv == 0) || (drv & 1) || (drv > BCD_ZERO)) /* bad drive #? */
198 return STOP_INVDSK;
199 drv = bcd_to_bin[drv] >> 1; /* convert */
200 uptr = dp_dev.units + drv; /* get unit ptr */
201 if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */
202 ind[IN_DSK] = ind[IN_ACC] = 1; /* no, error */
203 CRETIOE (iochk, SCPE_UNATT);
204 }
205
206 if ((fnc == FNC_SEEK) && /* seek and */
207 (M[dcf + DCF_DIR_FL] & DCF_DSEEK) == DCF_DSEEK) { /* direct flag? */
208 diff = dp_cvt_bcd (dcf + DCF_DIR, DCF_DIR_LEN); /* cvt diff */
209 if (diff < 0) return STOP_INVDSC; /* error? */
210 diff = diff >> 1; /* diff is *2 */
211 if ((M[dcf + DCF_DIR + DCF_DIR_LEN - 1] & ZONE) == BBIT)
212 diff = -diff; /* get sign */
213 uptr->CYL = uptr->CYL + diff; /* bound seek */
214 if (uptr->CYL < 0) uptr->CYL = 0;
215 else if (uptr->CYL >= DP_NUMCY) { /* too big? */
216 uptr->CYL = 0; /* system hangs */
217 return STOP_INVDCY;
218 }
219 sim_activate (&dp_unit[0], dp_time); /* set ctlr busy */
220 return SCPE_OK; /* done! */
221 }
222
223 sec = dp_cvt_bcd (dcf + DCF_SEC, DCF_SEC_LEN); /* cvt sector */
224 if ((sec < 0) || (sec >= (DP_NUMDR * DP_TOTSC))) /* bad sector? */
225 return STOP_INVDSC;
226 if (fnc == FNC_SEEK) { /* seek? */
227 uptr->CYL = (sec / (DP_NUMSF * DP_NUMSC)) % /* set cyl # */
228 DP_NUMCY;
229 sim_activate (&dp_unit[0], dp_time); /* set ctlr busy */
230 return SCPE_OK; /* done! */
231 }
232
233 BS = dcf + DCF_LEN; /* full DCF */
234 if (ADDR_ERR (BS)) return STOP_WRAP; /* DCF in memory? */
235 cnt = dp_get_cnt (dcf); /* get count */
236 if (cnt < 0) return STOP_INVDCN; /* bad count? */
237
238 if (fnc >= FNC_WOFF) return STOP_INVDFN; /* invalid func */
239 if (mod == BCD_W) { /* write? */
240 if (fnc == FNC_CHECK) { /* write check? */
241 qwc = 1; /* special read */
242 fnc = dp_lastf; /* use last func */
243 }
244 else {
245 dp_lastf = fnc; /* save func */
246 fnc = fnc + FNC_WOFF; /* change to write */
247 }
248 }
249 else if (mod == BCD_R) dp_lastf = fnc; /* read? save func */
250 else return STOP_INVM; /* other? error */
251
252 switch (fnc) { /* case on function */
253
254 case FNC_RSCO: /* read sec cnt ov */
255 BS = dcf + DCF_CNT; /* set count back */
256 /* fall thru */
257 case FNC_READ: /* read */
258 psec = dp_fndsec (uptr, sec, dcf); /* find sector */
259 if (psec < 0) CRETIOE (iochk, STOP_INVDAD); /* addr cmp error? */
260 for (;;) { /* loop */
261 qzr = (--cnt == 0); /* set zero latch */
262 dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* redo count */
263 if (r = dp_rdsec (uptr, psec, flg, qwc)) /* read sector */
264 break;
265 cnt = dp_get_cnt (dcf); /* get new count */
266 if (cnt < 0) return STOP_INVDCN; /* bad count? */
267 if (qzr) break; /* zero latch? done */
268 sec++; psec++; /* next sector */
269 dp_cvt_bin (dcf + DCF_SEC, DCF_SEC_LEN, sec, flg); /* rewr sec */
270 if (r = dp_nexsec (uptr, psec, dcf)) break; /* find next */
271 }
272 break; /* done, clean up */
273
274 case FNC_RTRK: /* read track */
275 AS = dcf + 9; /* special AS */
276 psec = dp_trkop (drv, sec); /* start of track */
277 for (;;) { /* loop */
278 qzr = (--cnt == 0); /* set zero latch */
279 dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* redo count */
280 if (r = dp_rdadr (uptr, psec, flg, qwc)) /* read addr */
281 break; /* error? */
282 if (r = dp_rdsec (uptr, psec, flg, qwc)) /* read data */
283 break; /* error? */
284 cnt = dp_get_cnt (dcf); /* get new count */
285 if (cnt < 0) return STOP_INVDCN; /* bad count? */
286 if (qzr) break; /* zero latch? done */
287 psec = dp_trkop (drv, sec) + ((psec + 1) % DP_NUMSC);
288 }
289 break; /* done, clean up */
290
291 case FNC_WRSCO: /* write sec cnt ov */
292 BS = dcf + DCF_CNT; /* set count back */
293 /* fall through */
294 case FNC_WRITE: /* read */
295 psec = dp_fndsec (uptr, sec, dcf); /* find sector */
296 if (psec < 0) CRETIOE (iochk, STOP_INVDAD); /* addr cmp error? */
297 for (;;) { /* loop */
298 qzr = (--cnt == 0); /* set zero latch */
299 dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* rewr cnt */
300 if (r = dp_wrsec (uptr, psec, flg)) break; /* write data */
301 if (qzr) break; /* zero latch? done */
302 sec++; psec++; /* next sector */
303 dp_cvt_bin (dcf + DCF_SEC, DCF_SEC_LEN, sec, flg); /* rewr sec */
304 if (r = dp_nexsec (uptr, psec, dcf)) break; /* find next */
305 }
306 break; /* done, clean up */
307
308 case FNC_WRTRK: /* write track */
309 if ((uptr->flags & UNIT_WAE) == 0) /* enabled? */
310 return STOP_WRADIS;
311 AS = dcf + 9; /* special AS */
312 psec = dp_trkop (drv, sec); /* start of track */
313 for (;;) { /* loop */
314 qzr = (--cnt == 0); /* set zero latch */
315 dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* redo count */
316 if (r = dp_wradr (uptr, psec, flg)) break; /* write addr */
317 if (r = dp_wrsec (uptr, psec, flg)) break; /* write data */
318 if (qzr) break; /* zero latch? done */
319 psec = dp_trkop (drv, sec) + ((psec + 1) % DP_NUMSC);
320 }
321 break; /* done, clean up */
322
323 default: /* unknown */
324 return STOP_INVDFN;
325 }
326
327 if (r == SCPE_OK) { /* normal so far? */
328 BS++; /* advance BS */
329 if (ADDR_ERR (BS)) return STOP_WRAP; /* address error? */
330 if (M[BS - 1] != (WM + BCD_GRPMRK)) { /* GM + WM at end? */
331 ind[IN_LNG] = ind[IN_DSK] = 1; /* no, error */
332 r = STOP_INVDLN;
333 }
334 }
335 CRETIOE (iochk || !ind[IN_DSK], r); /* return status */
336 }
337
338 /* Read or compare address with memory */
339
340 t_stat dp_rdadr (UNIT *uptr, int32 sec, int32 flg, int32 qwc)
341 {
342 int32 i;
343 uint8 ac;
344 int32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */
345 uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */
346 t_bool zad = dp_zeroad (ap); /* zero address */
347 static const int32 dec_tab[DP_ADDR] = { /* powers of 10 */
348 100000, 10000, 1000, 100, 10, 1
349 } ;
350
351 for (i = 0; i < DP_ADDR; i++) { /* copy address */
352 if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */
353 ind[IN_LNG] = ind[IN_DSK] = 1; /* error */
354 return STOP_INVDLN;
355 }
356 if (zad) { /* addr zero? */
357 ac = sec / dec_tab[i]; /* get addr digit */
358 sec = sec % dec_tab[i]; /* get remainder */
359 ac = bcd_to_bin[ac]; /* cvt to BCD */
360 }
361 else ac = *ap; /* addr char */
362 if (qwc) { /* wr chk? skip if zad */
363 if (!zad && (flg? (M[BS] != ac): /* L? cmp with WM */
364 ((M[BS] & CHAR) != (ac & CHAR)))) { /* M? cmp w/o WM */
365 ind[IN_DPW] = ind[IN_DSK] = 1;
366 return STOP_WRCHKE;
367 }
368 }
369 else if (flg) M[BS] = ac & CHAR; /* load mode */
370 else M[BS] = (M[BS] & WM) | (ac & CHAR); /* move mode */
371 ap++; BS++; /* adv ptrs */
372 if (ADDR_ERR (BS)) return STOP_WRAP;
373 }
374 return SCPE_OK;
375 }
376
377 /* Read or compare data with memory */
378
379 t_stat dp_rdsec (UNIT *uptr, int32 sec, int32 flg, int32 qwc)
380 {
381 int32 i, lim;
382 int32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */
383 uint8 *ap = ((uint8 *) uptr->filebuf) + da + DP_ADDR; /* buf ptr */
384
385 lim = flg? (DP_DATA - 10): DP_DATA; /* load vs move */
386 for (i = 0; i < lim; i++) { /* copy data */
387 if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */
388 ind[IN_LNG] = ind[IN_DSK] = 1; /* error */
389 return STOP_INVDLN;
390 }
391 if (qwc) { /* write check? */
392 if (flg? (M[BS] != *ap): /* load mode cmp */
393 ((M[BS] & CHAR) != (*ap & CHAR))) { /* move mode cmp */
394 ind[IN_DPW] = ind[IN_DSK] = 1; /* error */
395 return STOP_WRCHKE;
396 }
397 }
398 else if (flg) M[BS] = *ap & (WM | CHAR); /* load mode */
399 else M[BS] = (M[BS] & WM) | (*ap & CHAR); /* word mode */
400 ap++; BS++; /* adv ptrs */
401 if (ADDR_ERR (BS)) return STOP_WRAP;
402 }
403 return SCPE_OK;
404 }
405
406 /* Write address to disk */
407
408 t_stat dp_wradr (UNIT *uptr, int32 sec, int32 flg)
409 {
410 int32 i;
411 uint32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */
412 uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */
413
414 for (i = 0; i < DP_ADDR; i++) { /* copy address */
415 if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */
416 dp_fill (uptr, da, DP_NUMCH - i); /* fill, set err */
417 ind[IN_LNG] = ind[IN_DSK] = 1; /* error */
418 return STOP_INVDLN;
419 }
420 if (flg) *ap = M[BS] & (WM | CHAR); /* L? copy WM */
421 else *ap = M[BS] & CHAR; /* M? strip WM */
422 if (da >= uptr->hwmark) uptr->hwmark = da + 1;
423 da++; ap++; BS++; /* adv ptrs */
424 if (ADDR_ERR (BS)) return STOP_WRAP;
425 }
426 return SCPE_OK;
427 }
428
429 /* Write data to disk */
430
431 t_stat dp_wrsec (UNIT *uptr, int32 sec, int32 flg)
432 {
433 int32 i, lim;
434 uint32 da = ((sec % DP_TOTSC) * DP_NUMCH) + DP_ADDR; /* char number */
435 uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */
436
437 lim = flg? (DP_DATA - 10): DP_DATA; /* load vs move */
438 for (i = 0; i < lim; i++) { /* copy data */
439 if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */
440 dp_fill (uptr, da, DP_DATA - i); /* fill, set err */
441 ind[IN_LNG] = ind[IN_DSK] = 1; /* error */
442 return STOP_INVDLN;
443 }
444 if (flg) *ap = M[BS] & (WM | CHAR); /* load, copy WM */
445 else *ap = M[BS] & CHAR; /* move, strip WM */
446 if (da >= uptr->hwmark) uptr->hwmark = da + 1;
447 da++; ap++; BS++; /* adv ptrs */
448 if (ADDR_ERR (BS)) return STOP_WRAP;
449 }
450 return SCPE_OK;
451 }
452
453 /* Find sector */
454
455 int32 dp_fndsec (UNIT *uptr, int32 sec, int32 dcf)
456 {
457 int32 ctrk = sec % (DP_NUMSF * DP_NUMSC); /* curr trk-sec */
458 int32 psec = ((uptr->CYL) * (DP_NUMSF * DP_NUMSC)) + ctrk;
459 int32 da = psec * DP_NUMCH; /* char number */
460 uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */
461 int32 i;
462
463 if (dp_zeroad (ap)) return psec; /* addr zero? ok */
464 if (dp_cmp_ad (ap, dcf)) return psec; /* addr comp? ok */
465 psec = psec - (psec % DP_NUMSC); /* sector 0 */
466 for (i = 0; i < DP_NUMSC; i++, psec++) { /* check track */
467 da = psec * DP_NUMCH; /* char number */
468 ap = ((uint8 *) uptr->filebuf) + da; /* word pointer */
469 if (dp_zeroad (ap)) continue; /* no implicit match */
470 if (dp_cmp_ad (ap, dcf)) return psec; /* match? */
471 }
472 ind[IN_UNA] = ind[IN_DSK] = 1; /* no match */
473 return -1;
474 }
475
476 /* Find next sector - must be sequential, cannot cross cylinder boundary */
477
478 t_stat dp_nexsec (UNIT *uptr, int32 psec, int32 dcf)
479 {
480 int32 ctrk = psec % (DP_NUMSF * DP_NUMSC); /* curr trk-sec */
481 int32 da = psec * DP_NUMCH; /* word number */
482 uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */
483
484 if (ctrk) { /* not trk zero? */
485 if (dp_zeroad (ap)) return SCPE_OK; /* addr zero? ok */
486 if (dp_cmp_ad (ap, dcf)) return SCPE_OK; /* addr comp? ok */
487 }
488 ind[IN_UNA] = ind[IN_DSK] = 1; /* no, error */
489 return STOP_INVDAD;
490 }
491
492 /* Test for zero address */
493
494 t_bool dp_zeroad (uint8 *ap)
495 {
496 int32 i;
497
498 for (i = 0; i < DP_ADDR; i++, ap++) { /* loop thru addr */
499 if (*ap & CHAR) return FALSE; /* nonzero? lose */
500 }
501 return TRUE; /* all zeroes */
502 }
503
504 /* Compare disk address to memory sector address - always omit word marks */
505
506 t_bool dp_cmp_ad (uint8 *ap, int32 dcf)
507 {
508 int32 i;
509 uint8 c;
510
511 for (i = 0; i < DP_ADDR; i++, ap++) { /* loop thru addr */
512 c = M[dcf + DCF_SEC + i]; /* sector addr char */
513 if ((c & CHAR) != (*ap & CHAR)) /* cmp w/o WM */
514 return FALSE;
515 }
516 return TRUE; /* compare ok */
517 }
518
519 /* Track operation setup */
520
521 int32 dp_trkop (int32 drv, int32 sec)
522 {
523 int32 ctrk = (sec / DP_NUMSC) % DP_NUMSF;
524
525 return ((drv * DP_TOTSC) + (dp_unit[drv].CYL * DP_NUMSF * DP_NUMSC) +
526 (ctrk * DP_NUMSC));
527 }
528
529 /* Convert DCF BCD field to binary */
530
531 int32 dp_cvt_bcd (int32 ad, int32 len)
532 {
533 uint8 c;
534 int32 r;
535
536 for (r = 0; len > 0; len--) { /* loop thru char */
537 c = M[ad] & DIGIT; /* get digit */
538 if ((c == 0) || (c > BCD_ZERO)) return -1; /* invalid? */
539 r = (r * 10) + bcd_to_bin[c]; /* cvt to bin */
540 ad++; /* next digit */
541 }
542 return r;
543 }
544
545 /* Convert binary to DCF BCD field */
546
547 void dp_cvt_bin (int32 ad, int32 len, int32 val, int32 flg)
548 {
549 int32 r;
550
551 for ( ; len > 0; len--) { /* loop thru char */
552 r = val % 10; /* get digit */
553 if (flg) M[ad + len - 1] = bin_to_bcd[r]; /* load mode? */
554 else M[ad + len - 1] = (M[ad + len - 1] & WM) | bin_to_bcd[r];
555 val = val / 10;
556 }
557 return;
558 }
559
560 /* Get and validate count */
561
562 int32 dp_get_cnt (int32 dcf)
563 {
564 int32 cnt = dp_cvt_bcd (dcf + DCF_CNT, DCF_CNT_LEN); /* get new count */
565 if (cnt < 0) return -1; /* bad count? */
566 if (cnt == 0) return 1000; /* 0 => 1000 */
567 return cnt;
568 }
569
570 /* Fill sector buffer with blanks */
571
572 void dp_fill (UNIT *uptr, uint32 da, int32 cnt)
573 {
574 while (cnt-- > 0) { /* fill with blanks */
575 *(((uint8 *) uptr->filebuf) + da) = BCD_BLANK;
576 if (da >= uptr->hwmark) uptr->hwmark = da + 1;
577 da++;
578 }
579 return;
580 }
581
582 /* Reset routine */
583
584 t_stat dp_reset (DEVICE *dptr)
585 {
586 int32 i;
587
588 for (i = 0; i < DP_NUMDR; i++) dp_unit[i].CYL = 0; /* reset cylinder */
589 dp_lastf = 0; /* clear state */
590 ind[IN_DPW] = ind[IN_LNG] = ind[IN_UNA] = 0; /* clr indicators */
591 ind[IN_DSK] = ind[IN_ACC] = ind[IN_DBY] = 0;
592 sim_cancel (&dp_unit[0]); /* cancel timer */
593 return SCPE_OK;
594 }