1 /* i1620_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 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 (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
dp(uint32 op,uint32 pa,uint32 f0,uint32 f1)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) /* dcf must be even */
154 return STOP_INVDCF;
155 ind[IN_DACH] = ind[IN_DWLR] = 0; /* clr indicators */
156 ind[IN_DERR] = ind[IN_DCYO] = 0;
157 sa = ADDR_A (pa, DCF_SEC); /* ptr to sector */
158 if (((dp_unit[0].flags & UNIT_DIS) == 0) && /* only drive 0? */
159 (dp_unit[1].flags & UNIT_DIS) &&
160 (dp_unit[2].flags & UNIT_DIS) &&
161 (dp_unit[3].flags & UNIT_DIS)) drv = 0; /* ignore drv select */
162 else drv = (((M[pa] & 1)? M[pa]: M[sa]) & 0xE) >> 1; /* drive # */
163 if (drv >= DP_NUMDR) /* invalid? */
164 return STOP_INVDRV;
165 uptr = dp_dev.units + drv; /* get unit ptr */
166 if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */
167 ind[IN_DERR] = 1; /* no, error */
168 CRETIOE (dp_stop, SCPE_UNATT);
169 }
170
171 sec = dp_cvt_bcd (sa, DCF_SEC_LEN); /* cvt sector */
172 if ((sec < 0) || (sec >= (DP_NUMDR * DP_TOTSC))) /* bad sector? */
173 return STOP_INVDSC;
174 if (op == OP_K) { /* seek? */
175 if (f1 != FNC_SEEK) /* really? */
176 return STOP_INVFNC;
177 uptr->CYL = (sec / (DP_NUMSF * DP_NUMSC)) % /* set cyl # */
178 DP_NUMCY;
179 return SCPE_OK; /* done! */
180 }
181
182 cnt = dp_cvt_bcd (ADDR_A (pa, DCF_CNT), DCF_CNT_LEN); /* get count */
183 t = dp_cvt_bcd (ADDR_A (pa, DCF_ADR), DCF_ADR_LEN); /* get address */
184 if ((t < 0) || (t & 1)) /* bad address? */
185 return STOP_INVDBA;
186 dp_ba = t; /* save addr */
187
188 if (f1 >= FNC_WRI) /* invalid func? */
189 return STOP_INVFNC;
190 if (op == OP_RN) /* read? set wch */
191 qwc = f1 & FNC_WCH;
192 else if (op == OP_WN) { /* write? */
193 if (op & FNC_WCH) /* cant check */
194 return STOP_INVFNC;
195 f1 = f1 + FNC_WRI; /* offset fnc */
196 }
197 else return STOP_INVFNC; /* not R or W */
198 qnr = f1 & FNC_NRL; /* no rec check? */
199
200 switch (f1 & ~(FNC_WCH | FNC_NRL)) { /* case on function */
201
202 case FNC_SEC: /* read sectors */
203 if (cnt <= 0) /* bad count? */
204 return STOP_INVDCN;
205 psec = dp_fndsec (uptr, sec, TRUE); /* find sector */
206 if (psec < 0) /* error? */
207 CRETIOE (dp_stop, STOP_DACERR);
208 do { /* loop on count */
209 if ((r = dp_rdsec (uptr, psec, qnr, qwc))) /* read sector */
210 break;
211 sec++; psec++; /* next sector */
212 } while ((--cnt > 0) &&
213 ((r = dp_nexsec (uptr, sec, psec, TRUE)) == SCPE_OK));
214 break; /* done, clean up */
215
216 case FNC_TRK: /* read track */
217 psec = dp_trkop (drv, sec); /* start of track */
218 for (cnt = 0; cnt < DP_NUMSC; cnt++) { /* full track */
219 if ((r = dp_rdadr (uptr, psec, qnr, qwc))) /* read addr */
220 break; /* error? */
221 if ((r = dp_rdsec (uptr, psec, qnr, qwc))) /* read data */
222 break; /* error? */
223 psec = dp_trkop (drv, sec) + ((psec + 1) % DP_NUMSC);
224 }
225 break; /* done, clean up */
226
227 case FNC_SEC + FNC_WRI: /* write */
228 if (cnt <= 0) /* bad count? */
229 return STOP_INVDCN;
230 psec = dp_fndsec (uptr, sec, FALSE); /* find sector */
231 if (psec < 0) /* error? */
232 CRETIOE (dp_stop, STOP_DACERR);
233 do { /* loop on count */
234 if ((r = dp_tstgm (M[dp_ba], qnr))) /* start with gm? */
235 break;
236 if ((r = dp_wrsec (uptr, psec, qnr))) /* write data */
237 break;
238 sec++; psec++; /* next sector */
239 } while ((--cnt > 0) &&
240 ((r = dp_nexsec (uptr, sec, psec, FALSE)) == SCPE_OK));
241 break; /* done, clean up */
242
243 case FNC_TRK + FNC_WRI: /* write track */
244 if ((uptr->flags & UNIT_WAE) == 0) /* enabled? */
245 return STOP_WRADIS;
246 psec = dp_trkop (drv, sec); /* start of track */
247 for (cnt = 0; cnt < DP_NUMSC; cnt++) { /* full track */
248 if ((r = dp_tstgm (M[dp_ba], qnr))) /* start with gm? */
249 break;
250 if ((r = dp_wradr (uptr, psec, qnr))) /* write addr */
251 break;
252 if ((r = dp_wrsec (uptr, psec, qnr))) /* write data */
253 break;
254 psec = dp_trkop (drv, sec) + ((psec + 1) % DP_NUMSC);
255 }
256 break; /* done, clean up */
257
258 default: /* unknown */
259 return STOP_INVFNC;
260 }
261
262 if ((r == SCPE_OK) && !qnr) { /* eor check? */
263 if ((M[dp_ba] & DIGIT) != GRP_MARK) { /* GM at end? */
264 ind[IN_DWLR] = ind[IN_DERR] = 1; /* no, error */
265 r = STOP_WRLERR;
266 }
267 }
268 if ((r != SCPE_OK) && /* error? */
269 (dp_stop || !ind[IN_DERR])) /* iochk or stop? */
270 return r;
271 return SCPE_OK; /* continue */
272 }
273
274 /* Read or compare address with memory */
275
dp_rdadr(UNIT * uptr,int32 sec,int32 qnr,int32 qwc)276 t_stat dp_rdadr (UNIT *uptr, int32 sec, int32 qnr, int32 qwc)
277 {
278 int32 i;
279 uint8 ad;
280 int32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */
281 uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */
282 t_bool zad = dp_zeroad (ap); /* zero address */
283 static const int32 dec_tab[DP_ADDR] = { /* powers of 10 */
284 10000, 1000, 100, 10, 1
285 } ;
286
287 for (i = 0; i < DP_ADDR; i++) { /* copy/check addr */
288 if (zad) { /* addr zero? */
289 ad = sec / dec_tab[i]; /* get addr digit */
290 sec = sec % dec_tab[i]; /* get remainder */
291 }
292 else ad = *ap; /* addr digit */
293 if (qwc) { /* write check? */
294 if (dp_tstgm (M[dp_ba], qnr)) /* grp mrk in mem? */
295 return STOP_WRLERR; /* yes, error */
296 if (!zad && (M[dp_ba] != ad)) { /* digits equal? */
297 ind[IN_DACH] = ind[IN_DERR] = 1; /* no, error */
298 return STOP_DWCERR;
299 }
300 }
301 else M[dp_ba] = ad & (FLAG | DIGIT); /* store digit */
302 if (dp_tstgm (*ap, qnr)) /* grp mrk on disk? */
303 return STOP_WRLERR;
304 ap++; PP (dp_ba); /* adv ptrs */
305 }
306 return SCPE_OK;
307 }
308
309 /* Read or compare data with memory */
310
dp_rdsec(UNIT * uptr,int32 sec,int32 qnr,int32 qwc)311 t_stat dp_rdsec (UNIT *uptr, int32 sec, int32 qnr, int32 qwc)
312 {
313 int32 i;
314 int32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */
315 uint8 *ap = ((uint8 *) uptr->filebuf) + da + DP_ADDR; /* buf ptr */
316
317 for (i = 0; i < DP_DATA; i++) { /* copy data */
318 if (qwc) { /* write check? */
319 if (dp_tstgm (M[dp_ba], qnr)) /* grp mrk in mem? */
320 return STOP_WRLERR; /* yes, error */
321 if (M[dp_ba] != *ap) { /* dig+flags equal? */
322 ind[IN_DACH] = ind[IN_DERR] = 1; /* no, error */
323 return STOP_DWCERR;
324 }
325 }
326 else M[dp_ba] = *ap & (FLAG | DIGIT); /* flag + digit */
327 if (dp_tstgm (*ap, qnr)) /* grp mrk on disk? */
328 return STOP_WRLERR;
329 ap++; PP (dp_ba); /* adv ptrs */
330 }
331 return SCPE_OK;
332 }
333
334 /* Write address to disk */
335
dp_wradr(UNIT * uptr,int32 sec,int32 qnr)336 t_stat dp_wradr (UNIT *uptr, int32 sec, int32 qnr)
337 {
338 int32 i;
339 uint32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */
340 uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */
341
342 for (i = 0; i < DP_ADDR; i++) { /* copy address */
343 *ap = M[dp_ba] & (FLAG | DIGIT); /* flag + digit */
344 if (da >= uptr->hwmark)
345 uptr->hwmark = da + 1;
346 if (dp_tstgm (*ap, qnr)) { /* grp mrk fm mem? */
347 dp_fill (uptr, da + 1, DP_NUMCH - i - 1); /* fill addr+data */
348 return STOP_WRLERR; /* error */
349 }
350 da++; ap++; PP (dp_ba); /* adv ptrs */
351 }
352 return SCPE_OK;
353 }
354
355 /* Write data to disk */
356
dp_wrsec(UNIT * uptr,int32 sec,int32 qnr)357 t_stat dp_wrsec (UNIT *uptr, int32 sec, int32 qnr)
358 {
359 int32 i;
360 uint32 da = ((sec % DP_TOTSC) * DP_NUMCH) + DP_ADDR; /* char number */
361 uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */
362
363 for (i = 0; i < DP_DATA; i++) { /* copy data */
364 *ap = M[dp_ba] & (FLAG | DIGIT); /* get character */
365 if (da >= uptr->hwmark)
366 uptr->hwmark = da + 1;
367 if (dp_tstgm (*ap, qnr)) { /* grp mrk fm mem? */
368 dp_fill (uptr, da + 1, DP_DATA - i - 1); /* fill data */
369 return STOP_WRLERR; /* error */
370 }
371 da++; ap++; PP (dp_ba); /* adv ptrs */
372 }
373 return SCPE_OK;
374 }
375
376 /* Find sector */
377
dp_fndsec(UNIT * uptr,int32 sec,t_bool rd)378 int32 dp_fndsec (UNIT *uptr, int32 sec, t_bool rd)
379 {
380 int32 ctrk = sec % (DP_NUMSF * DP_NUMSC); /* curr trk-sec */
381 int32 psec = ((uptr->CYL) * (DP_NUMSF * DP_NUMSC)) + ctrk;
382 int32 da = psec * DP_NUMCH; /* char number */
383 uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */
384 int32 dskad, i;
385
386 if (dp_zeroad (ap)) /* addr zero? ok */
387 return psec;
388 dskad = dp_cvt_ad (ap); /* cvt addr */
389 if (dskad == sec) { /* match? */
390 if (rd || ((*ap & FLAG) == 0)) /* read or !wprot? */
391 return psec;
392 ind[IN_DACH] = ind[IN_DERR] = 1; /* no match */
393 return -1;
394 }
395 psec = psec - (psec % DP_NUMSC); /* sector 0 */
396 for (i = 0; i < DP_NUMSC; i++, psec++) { /* check track */
397 da = psec * DP_NUMCH; /* char number */
398 ap = ((uint8 *) uptr->filebuf) + da; /* word pointer */
399 if (dp_zeroad (ap)) /* no implicit match */
400 continue;
401 dskad = dp_cvt_ad (ap); /* cvt addr */
402 if (dskad == sec) { /* match? */
403 if (rd || ((*ap & FLAG) == 0)) /* read or !wprot? */
404 return psec;
405 ind[IN_DACH] = ind[IN_DERR] = 1; /* no match */
406 return -1;
407 }
408 }
409 ind[IN_DACH] = ind[IN_DERR] = 1; /* no match */
410 return -1;
411 }
412
413 /* Find next sector - must be sequential, cannot cross cylinder boundary */
414
dp_nexsec(UNIT * uptr,int32 sec,int32 psec,t_bool rd)415 t_stat dp_nexsec (UNIT *uptr, int32 sec, int32 psec, t_bool rd)
416 {
417 int32 ctrk = psec % (DP_NUMSF * DP_NUMSC); /* curr trk-sec */
418 int32 da = psec * DP_NUMCH; /* word number */
419 uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */
420 int32 dskad;
421
422 if (ctrk) { /* not trk zero? */
423 if (dp_zeroad (ap)) /* addr zero? ok */
424 return SCPE_OK;
425 dskad = dp_cvt_ad (ap); /* cvt addr */
426 if ((dskad == sec) && /* match? */
427 (rd || ((*ap & FLAG) == 0))) /* read or !wprot? */
428 return SCPE_OK;
429 ind[IN_DACH] = ind[IN_DERR] = 1; /* no, error */
430 return STOP_DACERR;
431 }
432 ind[IN_DCYO] = ind[IN_DERR] = 1; /* cyl overflow */
433 return STOP_CYOERR;
434 }
435
436 /* Test for zero address */
437
dp_zeroad(uint8 * ap)438 t_bool dp_zeroad (uint8 *ap)
439 {
440 int32 i;
441
442 for (i = 0; i < DP_ADDR; i++, ap++) { /* loop thru addr */
443 if (*ap & DIGIT) /* nonzero? lose */
444 return FALSE;
445 }
446 return TRUE; /* all zeroes */
447 }
448
449 /* Test for group mark when enabled */
450
dp_tstgm(uint32 c,int32 qnr)451 t_stat dp_tstgm (uint32 c, int32 qnr)
452 {
453 if (!qnr && ((c & DIGIT) == GRP_MARK)) { /* premature GM? */
454 ind[IN_DWLR] = ind[IN_DERR] = 1; /* error */
455 return STOP_WRLERR;
456 }
457 return SCPE_OK;
458 }
459
460 /* Convert disk address to binary - invalid char force bad address */
461
dp_cvt_ad(uint8 * ap)462 int32 dp_cvt_ad (uint8 *ap)
463 {
464 int32 i, r;
465 uint8 c;
466
467 for (i = r = 0; i < DP_ADDR; i++, ap++) { /* loop thru addr */
468 c = *ap & DIGIT; /* get digit */
469 if (BAD_DIGIT (c)) /* bad digit? */
470 return -1;
471 r = (r * 10) + c; /* bcd to binary */
472 }
473 return r;
474 }
475
476 /* Track operation setup */
477
dp_trkop(int32 drv,int32 sec)478 int32 dp_trkop (int32 drv, int32 sec)
479 {
480 int32 ctrk = (sec / DP_NUMSC) % DP_NUMSF;
481
482 return ((drv * DP_TOTSC) + (dp_unit[drv].CYL * DP_NUMSF * DP_NUMSC) +
483 (ctrk * DP_NUMSC));
484 }
485
486 /* Convert DCF BCD field to binary */
487
dp_cvt_bcd(uint32 ad,int32 len)488 int32 dp_cvt_bcd (uint32 ad, int32 len)
489 {
490 uint8 c;
491 int32 r;
492
493 for (r = 0; len > 0; len--) { /* loop thru char */
494 c = M[ad] & DIGIT; /* get digit */
495 if (BAD_DIGIT (c)) /* invalid? */
496 return -1;
497 r = (r * 10) + c; /* cvt to bin */
498 PP (ad); /* next digit */
499 }
500 return r;
501 }
502
503 /* Fill sector buffer with zero */
504
dp_fill(UNIT * uptr,uint32 da,int32 cnt)505 void dp_fill (UNIT *uptr, uint32 da, int32 cnt)
506 {
507 while (cnt-- > 0) { /* fill with zeroes*/
508 *(((uint8 *) uptr->filebuf) + da) = 0;
509 if (da >= uptr->hwmark)
510 uptr->hwmark = da + 1;
511 da++;
512 }
513 return;
514 }
515
516 /* Reset routine */
517
dp_reset(DEVICE * dptr)518 t_stat dp_reset (DEVICE *dptr)
519 {
520 int32 i;
521
522 for (i = 0; i < DP_NUMDR; i++) /* reset cylinder */
523 dp_unit[i].CYL = 0;
524 ind[IN_DACH] = ind[IN_DWLR] = 0; /* clr indicators */
525 ind[IN_DERR] = ind[IN_DCYO] = 0;
526 return SCPE_OK;
527 }
528