First Commit of my working state
[simh.git] / Interdata / id_fd.c
1 /* id_fd.c: Interdata floppy disk simulator
2
3 Copyright (c) 2001-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 fd M46-630 floppy disk
27
28 A diskette consists of 77 tracks, each with 26 sectors of 128B. The
29 Interdata floppy uses a logical record numbering scheme from 1 to 2002.
30 Physical tracks are numbered 0-76, physical sectors 1-26.
31
32 To allow for deleted data handling, a directory is appended to the end
33 of the image, one byte per LRN. Zero (the default) is a normal record,
34 non-zero a deleted record.
35 */
36
37 #include "id_defs.h"
38
39 #define FD_NUMTR 77 /* tracks/disk */
40 #define FD_NUMSC 26 /* sectors/track */
41 #define FD_NUMBY 128 /* bytes/sector */
42 #define FD_NUMLRN (FD_NUMTR * FD_NUMSC) /* LRNs/disk */
43 #define FD_SIZE (FD_NUMLRN * FD_NUMBY) /* bytes/disk */
44 #define FD_NUMDR 4 /* drives/controller */
45 #define UNIT_V_WLK (UNIT_V_UF) /* write locked */
46 #define UNIT_WLK (1u << UNIT_V_UF)
47 #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */
48 #define LRN u3 /* last LRN */
49 #define FNC u4 /* last function */
50 #define GET_DA(x) (((x) - 1) * FD_NUMBY)
51 #define GET_TRK(x) (((x) - 1) / FD_NUMSC)
52 #define GET_SEC(x) ((((x) - 1) % FD_NUMSC) + 1)
53 #define LRN_BOOT 5 /* boot block LRN */
54
55 /* Command byte */
56
57 #define CMD_V_UNIT 4 /* unit */
58 #define CMD_M_UNIT 0x3
59 #define GET_UNIT(x) (((x) >> CMD_V_UNIT) & CMD_M_UNIT)
60 #define CMD_V_FNC 0 /* function */
61 #define CMD_M_FNC 0xF
62 #define GET_FNC(x) (((x) >> CMD_V_FNC) & CMD_M_FNC)
63 #define FNC_RD 0x1 /* read */
64 #define FNC_WR 0x2 /* write */
65 #define FNC_RDID 0x3 /* read ID */
66 #define FNC_RSTA 0x4 /* read status */
67 #define FNC_DEL 0x5 /* write deleted */
68 #define FNC_BOOT 0x6 /* boot */
69 #define FNC_STOP 0x7 /* stop */
70 #define FNC_RESET 0x8 /* reset */
71 #define FNC_FMT 0x9 /* format NI */
72 #define FNC_STOPPING 0x10 /* stopping */
73
74 /* Status byte, * = dynamic */
75
76 #define STA_WRP 0x80 /* *write prot */
77 #define STA_DEF 0x40 /* def track NI */
78 #define STA_DEL 0x20 /* del record */
79 #define STA_ERR 0x10 /* error */
80 #define STA_IDL 0x02 /* idle */
81 #define STA_OFL 0x01 /* fault */
82 #define STA_MASK (STA_DEF|STA_DEL|STA_ERR|STA_BSY|STA_IDL)
83 #define SET_EX (STA_ERR) /* set EX */
84
85 /* Extended status, 6 bytes, * = dynamic */
86
87 #define ES_SIZE 6
88 #define ES0_HCRC 0x80 /* ID CRC NI */
89 #define ES0_DCRC 0x40 /* data CRC NI */
90 #define ES0_LRN 0x20 /* illegal LRN */
91 #define ES0_WRP 0x10 /* *write prot */
92 #define ES0_ERR 0x08 /* error */
93 #define ES0_DEF 0x04 /* def trk NI */
94 #define ES0_DEL 0x02 /* del rec NI */
95 #define ES0_FLT 0x01 /* fault */
96 #define ES1_TK0 0x80 /* track 0 */
97 #define ES1_NRDY 0x40 /* not ready */
98 #define ES1_NOAM 0x20 /* no addr mk NI */
99 #define ES1_CMD 0x10 /* illegal cmd */
100 #define ES1_SKE 0x08 /* seek err NI */
101 #define ES1_UNS 0x04 /* unsafe NI */
102 #define ES1_UNIT 0x03 /* unit # */
103
104 /* Processing options for commands */
105
106 #define C_RD 0x1 /* cmd reads disk */
107 #define C_WD 0x2 /* cmd writes disk */
108
109 extern uint32 int_req[INTSZ], int_enb[INTSZ];
110
111 uint32 fd_sta = 0; /* status */
112 uint32 fd_cmd = 0; /* command */
113 uint32 fd_db = 0; /* data buffer */
114 uint32 fd_bptr = 0; /* buffer pointer */
115 uint8 fdxb[FD_NUMBY] = { 0 }; /* sector buffer */
116 uint8 fd_es[FD_NUMDR][ES_SIZE] = { 0 }; /* ext status */
117 uint32 fd_lrn = 0; /* log rec # */
118 uint32 fd_wdv = 0; /* wd valid */
119 uint32 fd_stopioe = 1; /* stop on error */
120 uint32 fd_arm = 0; /* intr arm */
121 int32 fd_ctime = 100; /* command time */
122 int32 fd_stime = 10; /* seek, per LRN */
123 int32 fd_xtime = 1; /* tr set time */
124
125 static uint32 ctab[16] = {
126 0, C_RD, C_WD, 0, /* 0, rd, wr, 0 */
127 0, C_WD, C_RD, 0, /* 0, del, boot, 0 */
128 0, 0, 0, 0,
129 0, 0, 0, 0
130 };
131
132 DEVICE fd_dev;
133 uint32 fd (uint32 dev, uint32 op, uint32 dat);
134 t_stat fd_svc (UNIT *uptr);
135 t_stat fd_reset (DEVICE *dptr);
136 t_stat fd_clr (DEVICE *dptr);
137 t_stat fd_boot (int32 unitno, DEVICE *dptr);
138 t_bool fd_dte (UNIT *uptr, t_bool wr);
139 uint32 fd_crc (uint32 crc, uint32 dat, uint32 cnt);
140 void fd_done (uint32 u, uint32 nsta, uint32 nes0, uint32 nes1);
141 void sched_seek (UNIT *uptr, int32 newlrn);
142
143 /* FD data structures
144
145 fd_dev FD device descriptor
146 fd_unit FD unit list
147 fd_reg FD register list
148 fd_mod FD modifier list
149 */
150
151 DIB fd_dib = { d_FD, -1, v_FD, NULL, &fd, NULL };
152
153 UNIT fd_unit[] = {
154 { UDATA (&fd_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
155 UNIT_BUFABLE+UNIT_MUSTBUF, FD_SIZE + FD_NUMLRN) },
156 { UDATA (&fd_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
157 UNIT_BUFABLE+UNIT_MUSTBUF, FD_SIZE + FD_NUMLRN) },
158 { UDATA (&fd_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
159 UNIT_BUFABLE+UNIT_MUSTBUF, FD_SIZE + FD_NUMLRN) },
160 { UDATA (&fd_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
161 UNIT_BUFABLE+UNIT_MUSTBUF, FD_SIZE + FD_NUMLRN) }
162 };
163
164 REG fd_reg[] = {
165 { HRDATA (CMD, fd_cmd, 8) },
166 { HRDATA (STA, fd_sta, 8) },
167 { HRDATA (BUF, fd_db, 8) },
168 { HRDATA (LRN, fd_lrn, 16) },
169 { BRDATA (ESTA, fd_es, 16, 8, ES_SIZE * FD_NUMDR) },
170 { BRDATA (DBUF, fdxb, 16, 8, FD_NUMBY) },
171 { HRDATA (DBPTR, fd_bptr, 8) },
172 { FLDATA (WDV, fd_wdv, 0) },
173 { FLDATA (IREQ, int_req[l_FD], i_FD) },
174 { FLDATA (IENB, int_enb[l_FD], i_FD) },
175 { FLDATA (IARM, fd_arm, 0) },
176 { DRDATA (CTIME, fd_ctime, 24), PV_LEFT },
177 { DRDATA (STIME, fd_stime, 24), PV_LEFT },
178 { DRDATA (XTIME, fd_xtime, 24), PV_LEFT },
179 { FLDATA (STOP_IOE, fd_stopioe, 0) },
180 { URDATA (ULRN, fd_unit[0].LRN, 16, 16, 0, FD_NUMDR, REG_HRO) },
181 { URDATA (UFNC, fd_unit[0].FNC, 16, 8, 0, FD_NUMDR, REG_HRO) },
182 { HRDATA (DEVNO, fd_dib.dno, 8), REG_HRO },
183 { NULL }
184 };
185
186 MTAB fd_mod[] = {
187 { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },
188 { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },
189 { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
190 &set_dev, &show_dev, NULL },
191 { 0 }
192 };
193
194 DEVICE fd_dev = {
195 "FD", fd_unit, fd_reg, fd_mod,
196 FD_NUMDR, 16, 20, 1, 16, 8,
197 NULL, NULL, &fd_reset,
198 &fd_boot, NULL, NULL,
199 &fd_dib, DEV_DISABLE
200 };
201
202 /* Floppy disk: IO routine */
203
204 uint32 fd (uint32 dev, uint32 op, uint32 dat)
205 {
206 int32 u, t, fnc;
207 UNIT *uptr;
208
209 fnc = GET_FNC (fd_cmd); /* get fnc */
210 u = GET_UNIT (fd_cmd); /* get unit */
211 uptr = fd_dev.units + u;
212 switch (op) { /* case IO op */
213 case IO_ADR: /* select */
214 return BY; /* byte only */
215
216 case IO_RD: /* read */
217 if (fd_sta & (STA_IDL | STA_BSY)) return fd_db; /* idle, busy? */
218 if (fd_bptr < FD_NUMBY) fd_db = fdxb[fd_bptr++];/* get byte */
219 if (fd_bptr >= FD_NUMBY) { /* buf end? */
220 if (ctab[fnc] & C_RD) { /* disk read? */
221 sched_seek (uptr, uptr->LRN + 1); /* sched read */
222 fd_sta = fd_sta | STA_BSY; /* set busy */
223 }
224 else fd_bptr = 0; /* just wrap */
225 }
226 if ((ctab[fnc] & C_RD) && fd_arm) /* if rd & arm, */
227 SET_INT (v_FD); /* interrupt */
228 return fd_db; /* return buf */
229
230 case IO_WD: /* write */
231 if (fd_sta & STA_IDL) { /* idle? */
232 fd_lrn = ((fd_lrn << 8) | dat) & DMASK16; /* insert byte */
233 fd_wdv = 1;
234 break;
235 }
236 if (fd_bptr < FD_NUMBY) /* if room, */
237 fdxb[fd_bptr++] = fd_db = dat; /* store byte */
238 if (fd_bptr >= FD_NUMBY) { /* buf end? */
239 if (ctab[fnc] & C_WD) { /* disk write? */
240 sched_seek (uptr, uptr->LRN + 1); /* sched write */
241 fd_sta = fd_sta | STA_BSY; /* set busy */
242 }
243 else fd_bptr = 0; /* just wrap */
244 }
245 if ((ctab[fnc] & C_WD) && fd_arm) /* if wr & arm, */
246 SET_INT (v_FD); /* interrupt */
247 break;
248
249 case IO_SS: /* status */
250 t = fd_sta & STA_MASK; /* get status */
251 if ((uptr->flags & UNIT_ATT) == 0) t = t | STA_DU;
252 if (t & SET_EX) t = t | STA_EX; /* test for ex */
253 return t;
254
255 case IO_OC: /* command */
256 fd_arm = int_chg (v_FD, dat, fd_arm); /* upd int ctrl */
257 fnc = GET_FNC (dat); /* new fnc */
258 fd_cmd = dat; /* save cmd */
259 u = GET_UNIT (dat); /* get unit */
260 uptr = fd_dev.units + u;
261 if (fnc == FNC_STOP) { /* stop? */
262 uptr->FNC = uptr->FNC | FNC_STOPPING; /* flag stop */
263 if (sim_is_active (uptr)) break; /* busy? cont */
264 if (ctab[GET_FNC (uptr->FNC)] & C_WD) { /* write? */
265 sched_seek (uptr, uptr->LRN + 1); /* sched write */
266 fd_sta = fd_sta | STA_BSY; /* set busy */
267 }
268 else fd_done (u, 0, 0, 0); /* nrml done */
269 break;
270 }
271 else if (fd_sta & STA_IDL) { /* must be idle */
272 if (fnc != FNC_RSTA) { /* !rd status */
273 fd_sta = STA_BSY; /* busy, !idle */
274 fd_es[u][0] = 0;
275 fd_es[u][1] = u; /* init ext sta */
276 }
277 else fd_sta = (fd_sta & ~STA_IDL) | STA_BSY;
278 if (fnc == FNC_BOOT) t = LRN_BOOT; /* boot? fixed sec */
279 else if (fd_wdv) t = fd_lrn; /* valid data? use */
280 else t = uptr->LRN; /* use prev */
281 fd_wdv = 0; /* data invalid */
282 fd_bptr = 0; /* init buffer */
283 uptr->FNC = fnc; /* save function */
284 uptr->LRN = t; /* save LRN */
285 if (ctab[fnc] & C_RD) sched_seek (uptr, t); /* seek now? */
286 else sim_activate (uptr, fd_ctime); /* start cmd */
287 }
288 break;
289 }
290
291 return 0;
292 }
293
294 /* Unit service; the action to be taken depends on command */
295
296 t_stat fd_svc (UNIT *uptr)
297 {
298 uint32 i, u, tk, sc, crc, fnc, da;
299 uint8 *fbuf = uptr->filebuf;
300
301 u = uptr - fd_dev.units; /* get unit number */
302 fnc = GET_FNC (uptr->FNC); /* get function */
303 switch (fnc) { /* case on function */
304
305 case FNC_RESET: /* reset */
306 fd_clr (&fd_dev); /* clear device */
307 fd_done (u, 0, 0, 0); /* set idle */
308 return SCPE_OK;
309
310 case FNC_STOP: /* stop */
311 fd_done (u, 0, 0, 0); /* set idle */
312 return SCPE_OK;
313
314 case FNC_BOOT: /* boot, buf empty */
315 case FNC_RD: /* read, buf empty */
316 if (uptr->FNC & FNC_STOPPING) break; /* stopped? */
317 if (fd_dte (uptr, FALSE)) return SCPE_OK; /* xfr error? */
318 da = GET_DA (uptr->LRN); /* get disk addr */
319 for (i = 0; i < FD_NUMBY; i++) /* read sector */
320 fdxb[i] = fbuf[da + i];
321 if (fbuf[FD_SIZE + uptr->LRN - 1]) { /* deleted? set err */
322 fd_sta = fd_sta | STA_DEL;
323 fd_es[u][0] = fd_es[u][0] | ES0_DEL;
324 }
325 fd_es[u][2] = GET_SEC (uptr->LRN); /* set ext sec/trk */
326 fd_es[u][3] = GET_TRK (uptr->LRN);
327 fd_bptr = 0; /* init buf */
328 uptr->LRN = uptr->LRN + 1; /* next block */
329 break;
330
331 case FNC_WR: case FNC_DEL: /* write block */
332 if (fd_dte (uptr, TRUE)) return SCPE_OK; /* xfr error? */
333 if (fd_bptr) { /* any transfer? */
334 da = GET_DA (uptr->LRN); /* get disk addr */
335 for (i = fd_bptr; i < FD_NUMBY; i++) /* pad sector */
336 fdxb[i] = fd_db;
337 for (i = 0; i < FD_NUMBY; i++) /* write sector */
338 fbuf[da + i] = fdxb[i]; /* then dir */
339 fbuf[FD_SIZE + uptr->LRN - 1] = ((fnc == FNC_DEL)? 1: 0);
340 uptr->hwmark = uptr->capac; /* rewrite all */
341 fd_es[u][2] = GET_SEC (uptr->LRN); /* set ext sec/trk */
342 fd_es[u][3] = GET_TRK (uptr->LRN);
343 fd_bptr = 0; /* init buf */
344 uptr->LRN = uptr->LRN + 1; /* next block */
345 }
346 break;
347
348 case FNC_RSTA: /* read status */
349 if (uptr->flags & UNIT_WPRT) /* wr protected? */
350 fd_es[u][0] = fd_es[u][0] | ES0_WRP;
351 if (GET_TRK (uptr->LRN) == 0) /* on track 0? */
352 fd_es[u][1] = fd_es[u][1] | ES1_TK0;
353 if ((uptr->flags & UNIT_BUF) == 0) { /* not attached? */
354 fd_es[u][0] = fd_es[u][0] | ES0_FLT; /* set err */
355 fd_es[u][1] = fd_es[u][1] | ES1_NRDY;
356 }
357 for (i = 0; i < ES_SIZE; i++) fdxb[i] = fd_es[u][i]; /* copy to buf */
358 for (i = ES_SIZE; i < FD_NUMBY; i++) fdxb[i] = 0;
359 break;
360
361 case FNC_RDID: /* read ID */
362 if ((uptr->flags & UNIT_BUF) == 0) { /* not attached? */
363 fd_done (u, STA_ERR, ES0_ERR | ES0_FLT, ES1_NRDY);
364 return SCPE_OK;
365 }
366 for (i = 0; i < FD_NUMBY; i++) fdxb[i] = 0; /* clr buf */
367 tk = GET_TRK (uptr->LRN); /* get track */
368 sc = GET_SEC (uptr->LRN); /* get sector */
369 fdxb[0] = tk & 0xFF; /* store track */
370 fdxb[2] = sc & 0xFF; /* store sector */
371 crc = fd_crc (0xFFFF, 0xFE00, 8); /* CRC addr mark */
372 crc = fd_crc (crc, tk << 8, 16); /* CRC track */
373 crc = fd_crc (crc, sc << 8, 16); /* CRC sector */
374 fdxb[4] = (crc >> 8) & 0xFF; /* store CRC */
375 fdxb[5] = crc & 0xFF;
376 break;
377
378 case FNC_FMT: /* format */
379 default:
380 fd_done (u, STA_ERR, ES0_ERR, ES1_CMD); /* ill cmd */
381 uptr->LRN = 1; /* on track 0 */
382 return SCPE_OK;
383 }
384
385 if (uptr->FNC & FNC_STOPPING) { /* stopping? */
386 uptr->FNC = FNC_STOP; /* fnc = STOP */
387 sim_activate (uptr, fd_ctime); /* schedule */
388 }
389 fd_sta = fd_sta & ~STA_BSY; /* clear busy */
390 if (fd_arm) SET_INT (v_FD); /* if armed, int */
391 return SCPE_OK;
392 }
393
394 /* Schedule seek */
395
396 void sched_seek (UNIT *uptr, int32 newlrn)
397 {
398 int32 diff = newlrn - uptr->LRN; /* LRN diff */
399
400 if (diff < 0) diff = -diff; /* ABS */
401 if (diff < 10) diff = 10; /* MIN 10 */
402 sim_activate (uptr, diff * fd_stime); /* schedule */
403 return;
404 }
405
406 /* Command complete */
407
408 void fd_done (uint32 u, uint32 nsta, uint32 nes0, uint32 nes1)
409 {
410 fd_sta = (fd_sta | STA_IDL | nsta) & ~STA_BSY; /* set idle */
411 if (fd_arm) SET_INT (v_FD); /* if armed, int */
412 fd_es[u][0] = fd_es[u][0] | nes0; /* set ext state */
413 fd_es[u][1] = fd_es[u][1] | nes1;
414 return;
415 }
416
417 /* Test for data transfer error */
418
419 t_bool fd_dte (UNIT *uptr, t_bool wr)
420 {
421 uint32 u = uptr - fd_dev.units;
422
423 if ((uptr->flags & UNIT_BUF) == 0) { /* not attached? */
424 fd_done (u, STA_ERR, ES0_ERR | ES0_FLT, ES1_NRDY);
425 return TRUE;
426 }
427 if (wr && (uptr->flags & UNIT_WPRT)) { /* wr protected? */
428 fd_done (u, STA_ERR, ES0_ERR | ES0_WRP, 0);
429 return TRUE;
430 }
431 if ((uptr->LRN == 0) || (uptr->LRN > FD_NUMLRN)) { /* bad LRN? */
432 fd_done (u, STA_ERR, ES0_ERR | ES0_LRN, 0);
433 return TRUE;
434 }
435 return FALSE;
436 }
437
438 /* Header CRC calculation */
439
440 uint32 fd_crc (uint32 crc, uint32 dat, uint32 cnt)
441 {
442 uint32 i, wrk;
443
444 for (i = 0; i < cnt; i++) {
445 wrk = crc ^ dat;
446 crc = (crc << 1) & DMASK16;
447 if (wrk & SIGN16) crc = ((crc ^ 0x1020) + 1) & DMASK16;
448 dat = (dat << 1) & DMASK16;
449 }
450 return crc;
451 }
452
453 /* Reset routine */
454
455 t_stat fd_clr (DEVICE *dptr)
456 {
457 int32 i, j;
458 UNIT *uptr;
459
460 fd_sta = STA_IDL; /* idle */
461 fd_cmd = 0; /* clear state */
462 fd_db = 0;
463 fd_bptr = 0;
464 fd_lrn = 1;
465 fd_wdv = 0;
466 for (i = 0; i < FD_NUMBY; i++) fdxb[i] = 0; /* clr xfr buf */
467 for (i = 0; i < FD_NUMDR; i++) { /* loop thru units */
468 for (j = 0; j < ES_SIZE; j++) fd_es[i][j] = 0; /* clr ext sta */
469 fd_es[i][2] = 1; /* sector 1 */
470 uptr = fd_dev.units + i;
471 sim_cancel (uptr); /* stop drive */
472 uptr->LRN = 1; /* clear state */
473 uptr->FNC = 0;
474 }
475 return SCPE_OK;
476 }
477
478 t_stat fd_reset (DEVICE *dptr)
479 {
480 CLR_INT (v_FD); /* clear int */
481 CLR_ENB (v_FD); /* disable int */
482 fd_arm = 0; /* disarm int */
483 return fd_clr (dptr);;
484 }
485
486 /* Bootstrap routine */
487
488 #define BOOT_START 0x50
489 #define BOOT_LEN (sizeof (boot_rom) / sizeof (uint8))
490
491 static uint8 boot_rom[] = {
492 0xD5, 0x00, 0x00, 0xCF, /* ST: AL CF */
493 0x43, 0x00, 0x00, 0x80 /* BR 80 */
494 };
495
496 t_stat fd_boot (int32 unitno, DEVICE *dptr)
497 {
498 extern uint32 PC, dec_flgs;
499 extern uint16 decrom[];
500
501 if (decrom[0xD5] & dec_flgs) return SCPE_NOFNC; /* AL defined? */
502 IOWriteBlk (BOOT_START, BOOT_LEN, boot_rom); /* copy boot */
503 IOWriteB (AL_DEV, fd_dib.dno); /* set dev no */
504 IOWriteB (AL_IOC, 0x86 + (unitno << CMD_V_UNIT)); /* set dev cmd, unit num */
505 IOWriteB (AL_SCH, 0); /* clr sch dev no */
506 PC = BOOT_START;
507 return SCPE_OK;
508 }