1 /* pdp8_rl.c: RL8A cartridge disk simulator
2 
3    Copyright (c) 1993-2011, 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    rl           RL8A cartridge disk
27 
28    25-Oct-05    RMS     Fixed IOT 61 decode bug (David Gesswein)
29    16-Aug-05    RMS     Fixed C++ declaration and cast problems
30    04-Jan-04    RMS     Changed attach routine to use sim_fsize
31    25-Apr-03    RMS     Revised for extended file support
32    04-Oct-02    RMS     Added DIB, device number support
33    06-Jan-02    RMS     Changed enable/disable support
34    30-Nov-01    RMS     Cloned from RL11
35 
36    The RL8A is a four drive cartridge disk subsystem.  An RL01 drive
37    consists of 256 cylinders, each with 2 surfaces containing 40 sectors
38    of 256 bytes.  An RL02 drive has 512 cylinders.
39 
40    The RL8A controller has several serious complications.
41    - Seeking is relative to the current disk address; this requires
42      keeping accurate track of the current cylinder.
43    - The RL8A will not switch heads or cross cylinders during transfers.
44    - The RL8A operates in 8b and 12b mode, like the RX8E; in 12b mode, it
45      packs 2 12b words into 3 bytes, creating a 170 "word" sector with
46      one wasted byte.  Multi-sector transfers in 12b mode don't work.
47 */
48 
49 #include "pdp8_defs.h"
50 
51 /* Constants */
52 
53 #define RL_NUMBY        256                             /* 8b bytes/sector */
54 #define RL_NUMSC        40                              /* sectors/surface */
55 #define RL_NUMSF        2                               /* surfaces/cylinder */
56 #define RL_NUMCY        256                             /* cylinders/drive */
57 #define RL_NUMDR        4                               /* drives/controller */
58 #define RL_MAXFR        (1 << 12)                       /* max transfer */
59 #define RL01_SIZE       (RL_NUMCY*RL_NUMSF*RL_NUMSC*RL_NUMBY)  /* words/drive */
60 #define RL02_SIZE       (RL01_SIZE * 2)                 /* words/drive */
61 #define RL_BBMAP        014                             /* sector for bblk map */
62 #define RL_BBID         0123                            /* ID for bblk map */
63 
64 /* Flags in the unit flags word */
65 
66 #define UNIT_V_WLK      (UNIT_V_UF + 0)                 /* write lock */
67 #define UNIT_V_RL02     (UNIT_V_UF + 1)                 /* RL01 vs RL02 */
68 #define UNIT_V_AUTO     (UNIT_V_UF + 2)                 /* autosize enable */
69 #define UNIT_V_DUMMY    (UNIT_V_UF + 3)                 /* dummy flag */
70 #define UNIT_DUMMY      (1u << UNIT_V_DUMMY)
71 #define UNIT_WLK        (1u << UNIT_V_WLK)
72 #define UNIT_RL02       (1u << UNIT_V_RL02)
73 #define UNIT_AUTO       (1u << UNIT_V_AUTO)
74 #define UNIT_WPRT       (UNIT_WLK | UNIT_RO)            /* write protect */
75 
76 /* Parameters in the unit descriptor */
77 
78 #define TRK             u3                              /* current cylinder */
79 #define STAT            u4                              /* status */
80 
81 /* RLDS, NI = not implemented, * = kept in STAT, ^ = kept in TRK */
82 
83 #define RLDS_LOAD       0                               /* no cartridge */
84 #define RLDS_LOCK       5                               /* lock on */
85 #define RLDS_BHO        0000010                         /* brushes home NI */
86 #define RLDS_HDO        0000020                         /* heads out NI */
87 #define RLDS_CVO        0000040                         /* cover open NI */
88 #define RLDS_HD         0000100                         /* head select ^ */
89 #define RLDS_RL02       0000200                         /* RL02 */
90 #define RLDS_DSE        0000400                         /* drv sel err NI */
91 #define RLDS_VCK        0001000                         /* vol check * */
92 #define RLDS_WGE        0002000                         /* wr gate err * */
93 #define RLDS_SPE        0004000                         /* spin err * */
94 #define RLDS_STO        0010000                         /* seek time out NI */
95 #define RLDS_WLK        0020000                         /* wr locked */
96 #define RLDS_HCE        0040000                         /* hd curr err NI */
97 #define RLDS_WDE        0100000                         /* wr data err NI */
98 #define RLDS_ATT        (RLDS_HDO+RLDS_BHO+RLDS_LOCK)   /* att status */
99 #define RLDS_UNATT      (RLDS_CVO+RLDS_LOAD)            /* unatt status */
100 #define RLDS_ERR        (RLDS_WDE+RLDS_HCE+RLDS_STO+RLDS_SPE+RLDS_WGE+ \
101                          RLDS_VCK+RLDS_DSE)             /* errors bits */
102 
103 /* RLCSA, seek = offset/rw = address (also uptr->TRK) */
104 
105 #define RLCSA_DIR       04000                           /* direction */
106 #define RLCSA_HD        02000                           /* head select */
107 #define RLCSA_CYL       00777                           /* cyl offset */
108 #define GET_CYL(x)      ((x) & RLCSA_CYL)
109 #define GET_TRK(x)      ((((x) & RLCSA_CYL) * RL_NUMSF) + \
110                         (((x) & RLCSA_HD)? 1: 0))
111 #define GET_DA(x)       ((GET_TRK(x) * RL_NUMSC) + rlsa)
112 
113 /* RLCSB, function/unit select */
114 
115 #define RLCSB_V_FUNC    0                               /* function */
116 #define RLCSB_M_FUNC    07
117 #define  RLCSB_MNT      0
118 #define  RLCSB_CLRD     1
119 #define  RLCSB_GSTA     2
120 #define  RLCSB_SEEK     3
121 #define  RLCSB_RHDR     4
122 #define  RLCSB_WRITE    5
123 #define  RLCSB_READ     6
124 #define  RLCSB_RNOHDR   7
125 #define RLCSB_V_MEX     3                               /* memory extension */
126 #define RLCSB_M_MEX     07
127 #define RLCSB_V_DRIVE   6                               /* drive */
128 #define RLCSB_M_DRIVE   03
129 #define RLCSB_V_IE      8                               /* int enable */
130 #define RLCSB_IE        (1u << RLCSB_V_IE)
131 #define RLCSB_8B        01000                           /* 12b/8b */
132 #define RCLS_MNT        02000                           /* maint NI */
133 #define RLCSB_RW        0001777                         /* read/write */
134 #define GET_FUNC(x)     (((x) >> RLCSB_V_FUNC) & RLCSB_M_FUNC)
135 #define GET_MEX(x)      (((x) >> RLCSB_V_MEX) & RLCSB_M_MEX)
136 #define GET_DRIVE(x)    (((x) >> RLCSB_V_DRIVE) & RLCSB_M_DRIVE)
137 
138 /* RLSA, disk sector */
139 
140 #define RLSA_V_SECT     6                               /* sector */
141 #define RLSA_M_SECT     077
142 #define GET_SECT(x)     (((x) >> RLSA_V_SECT) & RLSA_M_SECT)
143 
144 /* RLER, error register */
145 
146 #define RLER_DRDY       00001                           /* drive ready */
147 #define RLER_DRE        00002                           /* drive error */
148 #define RLER_HDE        01000                           /* header error */
149 #define RLER_INCMP      02000                           /* incomplete */
150 #define RLER_ICRC       04000                           /* CRC error */
151 #define RLER_MASK       07003
152 
153 /* RLSI, silo register, used only in read header */
154 
155 #define RLSI_V_TRK      6                               /* track */
156 
157 extern uint16 M[];
158 extern int32 int_req;
159 extern UNIT cpu_unit;
160 
161 uint8 *rlxb = NULL;                                     /* xfer buffer */
162 int32 rlcsa = 0;                                        /* control/status A */
163 int32 rlcsb = 0;                                        /* control/status B */
164 int32 rlma = 0;                                         /* memory address */
165 int32 rlwc = 0;                                         /* word count */
166 int32 rlsa = 0;                                         /* sector address */
167 int32 rler = 0;                                         /* error register */
168 int32 rlsi = 0, rlsi1 = 0, rlsi2 = 0;                   /* silo queue */
169 int32 rl_lft = 0;                                       /* silo left/right */
170 int32 rl_done = 0;                                      /* done flag */
171 int32 rl_erf = 0;                                       /* error flag */
172 int32 rl_swait = 10;                                    /* seek wait */
173 int32 rl_rwait = 10;                                    /* rotate wait */
174 int32 rl_stopioe = 1;                                   /* stop on error */
175 
176 DEVICE rl_dev;
177 int32 rl60 (int32 IR, int32 AC);
178 int32 rl61 (int32 IR, int32 AC);
179 t_stat rl_svc (UNIT *uptr);
180 t_stat rl_reset (DEVICE *dptr);
181 void rl_set_done (int32 error);
182 t_stat rl_boot (int32 unitno, DEVICE *dptr);
183 t_stat rl_attach (UNIT *uptr, char *cptr);
184 t_stat rl_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);
185 t_stat rl_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc);
186 
187 /* RL8A data structures
188 
189    rl_dev       RL device descriptor
190    rl_unit      RL unit list
191    rl_reg       RL register list
192    rl_mod       RL modifier list
193 */
194 
195 DIB rl_dib = { DEV_RL, 2, { &rl60, &rl61 } };
196 
197 UNIT rl_unit[] = {
198     { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+
199              UNIT_ROABLE, RL01_SIZE) },
200     { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+
201              UNIT_ROABLE, RL01_SIZE) },
202     { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+
203              UNIT_ROABLE, RL01_SIZE) },
204     { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+
205              UNIT_ROABLE, RL01_SIZE) }
206     };
207 
208 REG rl_reg[] = {
209     { ORDATA (RLCSA, rlcsa, 12) },
210     { ORDATA (RLCSB, rlcsb, 12) },
211     { ORDATA (RLMA, rlma, 12) },
212     { ORDATA (RLWC, rlwc, 12) },
213     { ORDATA (RLSA, rlsa, 6) },
214     { ORDATA (RLER, rler, 12) },
215     { ORDATA (RLSI, rlsi, 16) },
216     { ORDATA (RLSI1, rlsi1, 16) },
217     { ORDATA (RLSI2, rlsi2, 16) },
218     { FLDATA (RLSIL, rl_lft, 0) },
219     { FLDATA (INT, int_req, INT_V_RL) },
220     { FLDATA (DONE, rl_done, INT_V_RL) },
221     { FLDATA (IE, rlcsb, RLCSB_V_IE) },
222     { FLDATA (ERR, rl_erf, 0) },
223     { DRDATA (STIME, rl_swait, 24), PV_LEFT },
224     { DRDATA (RTIME, rl_rwait, 24), PV_LEFT },
225     { URDATA (CAPAC, rl_unit[0].capac, 10, T_ADDR_W, 0,
226               RL_NUMDR, PV_LEFT + REG_HRO) },
227     { FLDATA (STOP_IOE, rl_stopioe, 0) },
228     { ORDATA (DEVNUM, rl_dib.dev, 6), REG_HRO },
229     { NULL }
230     };
231 
232 MTAB rl_mod[] = {
233     { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },
234     { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },
235     { UNIT_DUMMY, 0, NULL, "BADBLOCK", &rl_set_bad },
236     { (UNIT_RL02+UNIT_ATT), UNIT_ATT, "RL01", NULL, NULL },
237     { (UNIT_RL02+UNIT_ATT), (UNIT_RL02+UNIT_ATT), "RL02", NULL, NULL },
238     { (UNIT_AUTO+UNIT_RL02+UNIT_ATT), 0, "RL01", NULL, NULL },
239     { (UNIT_AUTO+UNIT_RL02+UNIT_ATT), UNIT_RL02, "RL02", NULL, NULL },
240     { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL },
241     { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL },
242     { (UNIT_AUTO+UNIT_RL02), 0, NULL, "RL01", &rl_set_size },
243     { (UNIT_AUTO+UNIT_RL02), UNIT_RL02, NULL, "RL02", &rl_set_size },
244     { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
245       &set_dev, &show_dev, NULL },
246     { 0 }
247     };
248 
249 DEVICE rl_dev = {
250     "RL", rl_unit, rl_reg, rl_mod,
251     RL_NUMDR, 8, 24, 1, 8, 8,
252     NULL, NULL, &rl_reset,
253     &rl_boot, &rl_attach, NULL,
254     &rl_dib, DEV_DISABLE | DEV_DIS
255     };
256 
257 /* IOT routines */
258 
rl60(int32 IR,int32 AC)259 int32 rl60 (int32 IR, int32 AC)
260 {
261 int32 curr, offs, newc, maxc;
262 UNIT *uptr;
263 
264 switch (IR & 07) {                                      /* case IR<9:11> */
265 
266     case 0:                                             /* RLDC */
267         rl_reset (&rl_dev);                             /* reset device */
268         break;
269 
270     case 1:                                             /* RLSD */
271         if (rl_done)                                    /* skip if done */
272             AC = IOT_SKP;
273         else AC = 0;
274         rl_done = 0;                                    /* clear done */
275         int_req = int_req & ~INT_RL;                    /* clear intr */
276         return AC;
277 
278     case 2:                                             /* RLMA */
279         rlma = AC;
280         break;
281 
282     case 3:                                             /* RLCA */
283         rlcsa = AC;
284         break;
285 
286     case 4:                                             /* RLCB */
287         rlcsb = AC;
288         rl_done = 0;                                    /* clear done */
289         rler = rl_erf = 0;                              /* clear errors */
290         int_req = int_req & ~INT_RL;                    /* clear intr */
291         rl_lft = 0;                                     /* clear silo ptr */
292         uptr = rl_dev.units + GET_DRIVE (rlcsb);        /* select unit */
293         switch (GET_FUNC (rlcsb)) {                     /* case on func */
294 
295         case RLCSB_CLRD:                                /* clear drive */
296             uptr->STAT = uptr->STAT & ~RLDS_ERR;        /* clear errors */
297         case RLCSB_MNT:                                 /* mnt */
298             rl_set_done (0);
299             break;
300 
301         case RLCSB_SEEK:                                /* seek */
302             curr = GET_CYL (uptr->TRK);                 /* current cylinder */
303             offs = GET_CYL (rlcsa);                     /* offset */
304             if (rlcsa & RLCSA_DIR) {                    /* in or out? */
305                 newc = curr + offs;                     /* out */
306                 maxc = (uptr->flags & UNIT_RL02)?
307                         RL_NUMCY * 2: RL_NUMCY;
308                 if (newc >= maxc) newc = maxc - 1;
309                 }
310             else {
311                 newc = curr - offs;                     /* in */
312                 if (newc < 0) newc = 0;
313                 }
314             uptr->TRK = newc | (rlcsa & RLCSA_HD);
315             sim_activate (uptr, rl_swait * abs (newc - curr));
316             break;
317 
318         default:                                        /* data transfer */
319             sim_activate (uptr, rl_swait);              /* activate unit */
320             break;
321             }                                           /* end switch func */
322         break;
323 
324     case 5:                                             /* RLSA */
325         rlsa = GET_SECT (AC);
326         break;
327 
328     case 6:                                             /* spare */
329         return 0;
330 
331     case 7:                                             /* RLWC */
332         rlwc = AC;
333         break;
334         }                                               /* end switch pulse */
335 
336 return 0;                                               /* clear AC */
337 }
338 
rl61(int32 IR,int32 AC)339 int32 rl61 (int32 IR, int32 AC)
340 {
341 int32 dat;
342 UNIT *uptr;
343 
344 switch (IR & 07) {                                      /* case IR<9:11> */
345 
346     case 0:                                             /* RRER */
347         uptr = rl_dev.units + GET_DRIVE (rlcsb);        /* select unit */
348         if (!sim_is_active (uptr) &&                    /* update drdy */
349             (uptr->flags & UNIT_ATT))
350             rler = rler | RLER_DRDY;
351         else rler = rler & ~RLER_DRDY;
352         dat = rler & RLER_MASK;
353         break;
354 
355     case 1:                                             /* RRWC */
356         dat = rlwc;
357         break;
358 
359     case 2:                                             /* RRCA */
360         dat = rlcsa;
361         break;
362 
363     case 3:                                             /* RRCB */
364         dat = rlcsb;
365         break;
366 
367     case 4:                                             /* RRSA */
368         dat = (rlsa << RLSA_V_SECT) & 07777;
369         break;
370 
371     case 5:                                             /* RRSI */
372         if (rl_lft) {                                   /* silo left? */
373             dat = (rlsi >> 8) & 0377;                   /* get left 8b */
374             rlsi = rlsi1;                               /* ripple */
375             rlsi1 = rlsi2;
376             }
377         else dat = rlsi & 0377;                         /* get right 8b */
378         rl_lft = rl_lft ^ 1;                            /* change side */
379         break;
380 
381     case 6:                                             /* spare */
382         return AC;
383 
384     case 7:                                             /* RLSE */
385         if (rl_erf)                                     /* skip if err */
386             dat = IOT_SKP | AC;
387         else dat = AC;
388         rl_erf = 0;
389         break;
390         }                                               /* end switch pulse */
391 
392 return dat;
393 }
394 
395 /* Service unit timeout
396 
397    If seek in progress, complete seek command
398    Else complete data transfer command
399 
400    The unit control block contains the function and cylinder for
401    the current command.
402 */
403 
rl_svc(UNIT * uptr)404 t_stat rl_svc (UNIT *uptr)
405 {
406 int32 err, wc, maxc;
407 int32 i, j, func, da, bc, wbc;
408 uint32 ma;
409 
410 func = GET_FUNC (rlcsb);                                /* get function */
411 if (func == RLCSB_GSTA) {                               /* get status? */
412     rlsi = uptr->STAT |
413         ((uptr->TRK & RLCSA_HD)? RLDS_HD: 0) |
414         ((uptr->flags & UNIT_ATT)? RLDS_ATT: RLDS_UNATT);
415     if (uptr->flags & UNIT_RL02)
416         rlsi = rlsi | RLDS_RL02;
417     if (uptr->flags & UNIT_WPRT)
418         rlsi = rlsi | RLDS_WLK;
419     rlsi2 = rlsi1 = rlsi;
420     rl_set_done (0);                                    /* done */
421     return SCPE_OK;
422     }
423 
424 if ((uptr->flags & UNIT_ATT) == 0) {                    /* attached? */
425     uptr->STAT = uptr->STAT | RLDS_SPE;                 /* spin error */
426     rl_set_done (RLER_INCMP);                           /* flag error */
427     return IORETURN (rl_stopioe, SCPE_UNATT);
428     }
429 
430 if ((func == RLCSB_WRITE) && (uptr->flags & UNIT_WPRT)) {
431     uptr->STAT = uptr->STAT | RLDS_WGE;                 /* write and locked */
432     rl_set_done (RLER_DRE);                             /* flag error */
433     return SCPE_OK;
434     }
435 
436 if (func == RLCSB_SEEK) {                               /* seek? */
437     rl_set_done (0);                                    /* done */
438     return SCPE_OK;
439     }
440 
441 if (func == RLCSB_RHDR) {                               /* read header? */
442     rlsi = (GET_TRK (uptr->TRK) << RLSI_V_TRK) | rlsa;
443     rlsi1 = rlsi2 = 0;
444     rl_set_done (0);                                    /* done */
445     return SCPE_OK;
446     }
447 
448 if (((func != RLCSB_RNOHDR) && (GET_CYL (uptr->TRK) != GET_CYL (rlcsa)))
449    || (rlsa >= RL_NUMSC)) {                             /* bad cyl or sector? */
450     rl_set_done (RLER_HDE | RLER_INCMP);                /* flag error */
451     return SCPE_OK;
452     }
453 
454 ma = (GET_MEX (rlcsb) << 12) | rlma;                    /* get mem addr */
455 da = GET_DA (rlcsa) * RL_NUMBY;                         /* get disk addr */
456 wc = 010000 - rlwc;                                     /* get true wc */
457 if (rlcsb & RLCSB_8B) {                                 /* 8b mode? */
458     bc = wc;                                            /* bytes to xfr */
459     maxc = (RL_NUMSC - rlsa) * RL_NUMBY;                /* max transfer */
460     if (bc > maxc)                                      /* trk ovrun? limit */
461         wc = bc = maxc;
462     }
463 else {
464     bc = ((wc * 3) + 1) / 2;                            /* 12b mode */
465     if (bc > RL_NUMBY) {                                /* > 1 sector */
466         bc = RL_NUMBY;                                  /* cap xfer */
467         wc = (RL_NUMBY * 2) / 3;
468         }
469     }
470 
471 err = fseek (uptr->fileref, da, SEEK_SET);
472 
473 if ((func >= RLCSB_READ) && (err == 0) &&               /* read (no hdr)? */
474     MEM_ADDR_OK (ma)) {                                 /* valid bank? */
475     i = fxread (rlxb, sizeof (int8), bc, uptr->fileref);
476     err = ferror (uptr->fileref);
477     for ( ; i < bc; i++)                                /* fill buffer */
478         rlxb[i] = 0;
479     for (i = j = 0; i < wc; i++) {                      /* store buffer */
480         if (rlcsb & RLCSB_8B)                           /* 8b mode? */
481             M[ma] = rlxb[i] & 0377;                     /* store */
482         else if (i & 1) {                               /* odd wd 12b? */
483             M[ma] = ((rlxb[j + 1] >> 4) & 017) |
484                 (((uint16) rlxb[j + 2]) << 4);
485             j = j + 3;
486             }
487         else M[ma] = rlxb[j] |                          /* even wd 12b */
488             ((((uint16) rlxb[j + 1]) & 017) << 8);
489         ma = (ma & 070000) + ((ma + 1) & 07777);
490         }                                               /* end for */
491     }                                                   /* end if wr */
492 
493 if ((func == RLCSB_WRITE) && (err == 0)) {              /* write? */
494     for (i = j = 0; i < wc; i++) {                      /* fetch buffer */
495         if (rlcsb & RLCSB_8B)                           /* 8b mode? */
496             rlxb[i] = M[ma] & 0377;                     /* fetch */
497         else if (i & 1) {                               /* odd wd 12b? */
498             rlxb[j + 1] = rlxb[j + 1] | ((M[ma] & 017) << 4);
499             rlxb[j + 2] = ((M[ma] >> 4) & 0377);
500             j = j + 3;
501             }
502         else {                                          /* even wd 12b */
503             rlxb[j] = M[ma] & 0377;
504             rlxb[j + 1] = (M[ma] >> 8) & 017;
505             }
506         ma = (ma & 070000) + ((ma + 1) & 07777);
507         }                                               /* end for */
508     wbc = (bc + (RL_NUMBY - 1)) & ~(RL_NUMBY - 1);      /* clr to */
509     for (i = bc; i < wbc; i++)                          /* end of blk */
510         rlxb[i] = 0;
511     fxwrite (rlxb, sizeof (int8), wbc, uptr->fileref);
512     err = ferror (uptr->fileref);
513     }                                                   /* end write */
514 
515 rlwc = (rlwc + wc) & 07777;                             /* final word count */
516 if (rlwc != 0)                                          /* completed? */
517     rler = rler | RLER_INCMP;
518 rlma = (rlma + wc) & 07777;                             /* final word addr */
519 rlsa = rlsa + ((bc + (RL_NUMBY - 1)) / RL_NUMBY);
520 rl_set_done (0);
521 
522 if (err != 0) {                                         /* error? */
523     perror ("RL I/O error");
524     clearerr (uptr->fileref);
525     return SCPE_IOERR;
526     }
527 return SCPE_OK;
528 }
529 
530 /* Set done and possibly errors */
531 
rl_set_done(int32 status)532 void rl_set_done (int32 status)
533 {
534 rl_done = 1;
535 rler = rler | status;
536 if (rler)
537     rl_erf = 1;
538 if (rlcsb & RLCSB_IE)
539     int_req = int_req | INT_RL;
540 else int_req = int_req & ~INT_RL;
541 return;
542 }
543 
544 /* Device reset
545 
546    Note that the RL8A does NOT recalibrate its drives on RESET
547 */
548 
rl_reset(DEVICE * dptr)549 t_stat rl_reset (DEVICE *dptr)
550 {
551 int32 i;
552 UNIT *uptr;
553 
554 rlcsa = rlcsb = rlsa = rler = 0;
555 rlma = rlwc = 0;
556 rlsi = rlsi1 = rlsi2 = 0;
557 rl_lft = 0;
558 rl_done = 0;
559 rl_erf = 0;
560 int_req = int_req & ~INT_RL;
561 for (i = 0; i < RL_NUMDR; i++) {
562     uptr = rl_dev.units + i;
563     sim_cancel (uptr);
564     uptr->STAT = 0;
565     }
566 if (rlxb == NULL)
567     rlxb = (uint8 *) calloc (RL_MAXFR, sizeof (uint8));
568 if (rlxb == NULL)
569     return SCPE_MEM;
570 return SCPE_OK;
571 }
572 
573 /* Attach routine */
574 
rl_attach(UNIT * uptr,char * cptr)575 t_stat rl_attach (UNIT *uptr, char *cptr)
576 {
577 uint32 p;
578 t_stat r;
579 
580 uptr->capac = (uptr->flags & UNIT_RL02)? RL02_SIZE: RL01_SIZE;
581 r = attach_unit (uptr, cptr);                           /* attach unit */
582 if (r != SCPE_OK)                                       /* error? */
583     return r;
584 uptr->TRK = 0;                                          /* cyl 0 */
585 uptr->STAT = RLDS_VCK;                                  /* new volume */
586 if ((p = sim_fsize (uptr->fileref)) == 0) {             /* new disk image? */
587     if (uptr->flags & UNIT_RO)
588         return SCPE_OK;
589     return rl_set_bad (uptr, 0, NULL, NULL);
590     }
591 if ((uptr->flags & UNIT_AUTO) == 0)                     /* autosize? */
592     return r;
593 if (p > (RL01_SIZE * sizeof (int16))) {
594     uptr->flags = uptr->flags | UNIT_RL02;
595     uptr->capac = RL02_SIZE;
596     }
597 else {
598     uptr->flags = uptr->flags & ~UNIT_RL02;
599     uptr->capac = RL01_SIZE;
600     }
601 return SCPE_OK;
602 }
603 
604 /* Set size routine */
605 
rl_set_size(UNIT * uptr,int32 val,char * cptr,void * desc)606 t_stat rl_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)
607 {
608 if (uptr->flags & UNIT_ATT)
609     return SCPE_ALATT;
610 uptr->capac = (val & UNIT_RL02)? RL02_SIZE: RL01_SIZE;
611 return SCPE_OK;
612 }
613 
614 /* Factory bad block table creation routine
615 
616    This routine writes the OS/8 specific bad block map in track 0, sector 014 (RL_BBMAP):
617 
618         words 0 magic number = 0123 (RL_BBID)
619         words 1-n       block numbers
620          :
621         words n+1       end of table = 0
622 
623    Inputs:
624         uptr    =       pointer to unit
625         val     =       ignored
626    Outputs:
627         sta     =       status code
628 */
629 
rl_set_bad(UNIT * uptr,int32 val,char * cptr,void * desc)630 t_stat rl_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc)
631 {
632 int32 i, da = RL_BBMAP * RL_NUMBY;
633 
634 if ((uptr->flags & UNIT_ATT) == 0)
635     return SCPE_UNATT;
636 if (uptr->flags & UNIT_RO)
637     return SCPE_RO;
638 if (!get_yn ("Create bad block table? [N]", FALSE))
639     return SCPE_OK;
640 if (fseek (uptr->fileref, da, SEEK_SET))
641     return SCPE_IOERR;
642 rlxb[0] = RL_BBID;
643 for (i = 1; i < RL_NUMBY; i++)
644     rlxb[i] = 0;
645 fxwrite (rlxb, sizeof (uint8), RL_NUMBY, uptr->fileref);
646 if (ferror (uptr->fileref))
647     return SCPE_IOERR;
648 return SCPE_OK;
649 }
650 
651 /* Bootstrap */
652 
653 #define BOOT_START 1                                    /* start */
654 #define BOOT_UNIT 02006                                 /* unit number */
655 #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16))
656 
657 static const uint16 boot_rom[] = {
658     06600,                      /* BT, RLDC             ; reset */
659     07201,                      /* 02, CLA IAC          ; clr drv = 1 */
660     04027,                      /* 03, JMS GO           ; do io */
661     01004,                      /* 04, TAD 4            ; rd hdr fnc */
662     04027,                      /* 05, JMS GO           ; do io */
663     06615,                      /* 06, RRSI             ; rd hdr lo */
664     07002,                      /* 07, BSW              ; swap */
665     07012,                      /* 10, RTR              ; lo cyl to L */
666     06615,                      /* 11, RRSI             ; rd hdr hi */
667     00025,                      /* 12, AND 25           ; mask = 377 */
668     07004,                      /* 13, RTL              ; get cyl */
669     06603,                      /* 14, RLCA             ; set addr */
670     07325,                      /* 15, CLA STL IAC RAL  ; seek = 3 */
671     04027,                      /* 16, JMS GO           ; do io */
672     07332,                      /* 17, CLA STL RTR      ; dir in = 2000 */
673     06605,                      /* 20, RLSA             ; sector */
674     01026,                      /* 21, TAD (-200)       ; one sector */
675     06607,                      /* 22, RLWC             ; word cnt */
676     07327,                      /* 23, CLA STL IAC RTL  ; read = 6*/
677     04027,                      /* 24, JMS GO           ; do io */
678     00377,                      /* 25, JMP 377          ; start */
679     07600,                      /* 26, -200             ; word cnt */
680     00000,                      /* GO, 0                ; subr */
681     06604,                      /* 30, RLCB             ; load fnc */
682     06601,                      /* 31, RLSD             ; wait */
683     05031,                      /* 32, JMP .-1          ; */
684     06617,                      /* 33, RLSE             ; error? */
685     05427,                      /* 34, JMP I GO         ; no, ok */
686     05001                       /* 35, JMP BT           ; restart */
687     };
688 
689 
rl_boot(int32 unitno,DEVICE * dptr)690 t_stat rl_boot (int32 unitno, DEVICE *dptr)
691 {
692 int32 i;
693 extern int32 saved_PC;
694 
695 if (unitno)                                             /* only unit 0 */
696     return SCPE_ARG;
697 if (rl_dib.dev != DEV_RL)                               /* only std devno */
698     return STOP_NOTSTD;
699 rl_unit[unitno].TRK = 0;
700 for (i = 0; i < BOOT_LEN; i++)
701     M[BOOT_START + i] = boot_rom[i];
702 saved_PC = BOOT_START;
703 return SCPE_OK;
704 }
705