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