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