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