First Commit of my working state
[simh.git] / PDP8 / pdp8_rx.c
1 /* pdp8_rx.c: RX8E/RX01, RX28/RX02 floppy disk simulator
2
3 Copyright (c) 1993-2006, 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 rx RX8E/RX01, RX28/RX02 floppy disk
27
28 15-May-06 RMS Fixed bug in autosize attach (reported by Dave Gesswein)
29 04-Jan-04 RMS Changed sim_fsize calling sequence
30 05-Nov-03 RMS Fixed bug in RX28 read status (found by Charles Dickman)
31 26-Oct-03 RMS Cleaned up buffer copy code, fixed double density write
32 25-Apr-03 RMS Revised for extended file support
33 14-Mar-03 RMS Fixed variable size interaction with save/restore
34 03-Mar-03 RMS Fixed autosizing
35 08-Oct-02 RMS Added DIB, device number support
36 Fixed reset to work with disabled device
37 15-Sep-02 RMS Added RX28/RX02 support
38 06-Jan-02 RMS Changed enable/disable support
39 30-Nov-01 RMS Added read only unit, extended SET/SHOW support
40 24-Nov-01 RMS Converted FLG to array
41 17-Jul-01 RMS Fixed warning from VC++ 6
42 26-Apr-01 RMS Added device enable/disable support
43 13-Apr-01 RMS Revised for register arrays
44 14-Apr-99 RMS Changed t_addr to unsigned
45 15-Aug-96 RMS Fixed bug in LCD
46
47 An RX01 diskette consists of 77 tracks, each with 26 sectors of 128B.
48 An RX02 diskette consists of 77 tracks, each with 26 sectors of 128B
49 (single density) or 256B (double density). Tracks are numbered 0-76,
50 sectors 1-26. The RX8E (RX28) can store data in 8b mode or 12b mode.
51 In 8b mode, the controller reads or writes 128 bytes (128B or 256B)
52 per sector. In 12b mode, it reads or writes 64 (64 or 128) 12b words
53 per sector. The 12b words are bit packed into the first 96 (192) bytes
54 of the sector; the last 32 (64) bytes are zeroed on writes.
55 */
56
57 #include "pdp8_defs.h"
58
59 #define RX_NUMTR 77 /* tracks/disk */
60 #define RX_M_TRACK 0377
61 #define RX_NUMSC 26 /* sectors/track */
62 #define RX_M_SECTOR 0177 /* cf Jones!! */
63 #define RX_NUMBY 128 /* bytes/sector */
64 #define RX2_NUMBY 256
65 #define RX_NUMWD (RX_NUMBY / 2) /* words/sector */
66 #define RX2_NUMWD (RX2_NUMBY / 2)
67 #define RX_SIZE (RX_NUMTR * RX_NUMSC * RX_NUMBY) /* bytes/disk */
68 #define RX2_SIZE (RX_NUMTR * RX_NUMSC * RX2_NUMBY)
69 #define RX_NUMDR 2 /* drives/controller */
70 #define RX_M_NUMDR 01
71 #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */
72 #define UNIT_V_DEN (UNIT_V_UF + 1) /* double density */
73 #define UNIT_V_AUTO (UNIT_V_UF + 2) /* autosize */
74 #define UNIT_WLK (1u << UNIT_V_WLK)
75 #define UNIT_DEN (1u << UNIT_V_DEN)
76 #define UNIT_AUTO (1u << UNIT_V_AUTO)
77 #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */
78
79 #define IDLE 0 /* idle state */
80 #define CMD8 1 /* 8b cmd, ho next */
81 #define RWDS 2 /* rw, sect next */
82 #define RWDT 3 /* rw, track next */
83 #define RWXFR 4 /* rw, transfer */
84 #define FILL 5 /* fill buffer */
85 #define EMPTY 6 /* empty buffer */
86 #define SDCNF 7 /* set dens, conf next */
87 #define SDXFR 8 /* set dens, transfer */
88 #define CMD_COMPLETE 9 /* set done next */
89 #define INIT_COMPLETE 10 /* init compl next */
90
91 #define RXCS_V_FUNC 1 /* function */
92 #define RXCS_M_FUNC 7
93 #define RXCS_FILL 0 /* fill buffer */
94 #define RXCS_EMPTY 1 /* empty buffer */
95 #define RXCS_WRITE 2 /* write sector */
96 #define RXCS_READ 3 /* read sector */
97 #define RXCS_SDEN 4 /* set density (RX28) */
98 #define RXCS_RXES 5 /* read status */
99 #define RXCS_WRDEL 6 /* write del data */
100 #define RXCS_ECODE 7 /* read error code */
101 #define RXCS_DRV 0020 /* drive */
102 #define RXCS_MODE 0100 /* mode */
103 #define RXCS_MAINT 0200 /* maintenance */
104 #define RXCS_DEN 0400 /* density (RX28) */
105 #define RXCS_GETFNC(x) (((x) >> RXCS_V_FUNC) & RXCS_M_FUNC)
106
107 #define RXES_CRC 0001 /* CRC error NI */
108 #define RXES_ID 0004 /* init done */
109 #define RXES_RX02 0010 /* RX02 (RX28) */
110 #define RXES_DERR 0020 /* density err (RX28) */
111 #define RXES_DEN 0040 /* density (RX28) */
112 #define RXES_DD 0100 /* deleted data */
113 #define RXES_DRDY 0200 /* drive ready */
114
115 #define TRACK u3 /* current track */
116 #define READ_RXDBR ((rx_csr & RXCS_MODE)? AC | (rx_dbr & 0377): rx_dbr)
117 #define CALC_DA(t,s,b) (((t) * RX_NUMSC) + ((s) - 1)) * b
118
119 extern int32 int_req, int_enable, dev_done;
120
121 int32 rx_28 = 0; /* controller type */
122 int32 rx_tr = 0; /* xfer ready flag */
123 int32 rx_err = 0; /* error flag */
124 int32 rx_csr = 0; /* control/status */
125 int32 rx_dbr = 0; /* data buffer */
126 int32 rx_esr = 0; /* error status */
127 int32 rx_ecode = 0; /* error code */
128 int32 rx_track = 0; /* desired track */
129 int32 rx_sector = 0; /* desired sector */
130 int32 rx_state = IDLE; /* controller state */
131 int32 rx_cwait = 100; /* command time */
132 int32 rx_swait = 10; /* seek, per track */
133 int32 rx_xwait = 1; /* tr set time */
134 int32 rx_stopioe = 0; /* stop on error */
135 uint8 rx_buf[RX2_NUMBY] = { 0 }; /* sector buffer */
136 int32 rx_bptr = 0; /* buffer pointer */
137
138 DEVICE rx_dev;
139 int32 rx (int32 IR, int32 AC);
140 t_stat rx_svc (UNIT *uptr);
141 t_stat rx_reset (DEVICE *dptr);
142 t_stat rx_boot (int32 unitno, DEVICE *dptr);
143 t_stat rx_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);
144 t_stat rx_attach (UNIT *uptr, char *cptr);
145 void rx_cmd (void);
146 void rx_done (int32 esr_flags, int32 new_ecode);
147 t_stat rx_settype (UNIT *uptr, int32 val, char *cptr, void *desc);
148 t_stat rx_showtype (FILE *st, UNIT *uptr, int32 val, void *desc);
149
150 /* RX8E data structures
151
152 rx_dev RX device descriptor
153 rx_unit RX unit list
154 rx_reg RX register list
155 rx_mod RX modifier list
156 */
157
158 DIB rx_dib = { DEV_RX, 1, { &rx } };
159
160 UNIT rx_unit[] = {
161 { UDATA (&rx_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF+
162 UNIT_ROABLE, RX_SIZE) },
163 { UDATA (&rx_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF+
164 UNIT_ROABLE, RX_SIZE) }
165 };
166
167 REG rx_reg[] = {
168 { ORDATA (RXCS, rx_csr, 12) },
169 { ORDATA (RXDB, rx_dbr, 12) },
170 { ORDATA (RXES, rx_esr, 12) },
171 { ORDATA (RXERR, rx_ecode, 8) },
172 { ORDATA (RXTA, rx_track, 8) },
173 { ORDATA (RXSA, rx_sector, 8) },
174 { DRDATA (STAPTR, rx_state, 4), REG_RO },
175 { DRDATA (BUFPTR, rx_bptr, 8) },
176 { FLDATA (TR, rx_tr, 0) },
177 { FLDATA (ERR, rx_err, 0) },
178 { FLDATA (DONE, dev_done, INT_V_RX) },
179 { FLDATA (ENABLE, int_enable, INT_V_RX) },
180 { FLDATA (INT, int_req, INT_V_RX) },
181 { DRDATA (CTIME, rx_cwait, 24), PV_LEFT },
182 { DRDATA (STIME, rx_swait, 24), PV_LEFT },
183 { DRDATA (XTIME, rx_xwait, 24), PV_LEFT },
184 { FLDATA (STOP_IOE, rx_stopioe, 0) },
185 { BRDATA (SBUF, rx_buf, 8, 8, RX2_NUMBY) },
186 { FLDATA (RX28, rx_28, 0), REG_HRO },
187 { URDATA (CAPAC, rx_unit[0].capac, 10, T_ADDR_W, 0,
188 RX_NUMDR, REG_HRO | PV_LEFT) },
189 { ORDATA (DEVNUM, rx_dib.dev, 6), REG_HRO },
190 { NULL }
191 };
192
193 MTAB rx_mod[] = {
194 { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },
195 { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },
196 { MTAB_XTD | MTAB_VDV, 1, NULL, "RX28", &rx_settype, NULL, NULL },
197 { MTAB_XTD | MTAB_VDV, 0, NULL, "RX8E", &rx_settype, NULL, NULL },
198 { MTAB_XTD | MTAB_VDV, 0, "TYPE", NULL, NULL, &rx_showtype, NULL },
199 { (UNIT_DEN+UNIT_ATT), UNIT_ATT, "single density", NULL, NULL },
200 { (UNIT_DEN+UNIT_ATT), (UNIT_DEN+UNIT_ATT), "double density", NULL, NULL },
201 { (UNIT_AUTO+UNIT_DEN+UNIT_ATT), 0, "single density", NULL, NULL },
202 { (UNIT_AUTO+UNIT_DEN+UNIT_ATT), UNIT_DEN, "double density", NULL, NULL },
203 { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL },
204 { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL },
205 { (UNIT_AUTO+UNIT_DEN), 0, NULL, "SINGLE", &rx_set_size },
206 { (UNIT_AUTO+UNIT_DEN), UNIT_DEN, NULL, "DOUBLE", &rx_set_size },
207 { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
208 &set_dev, &show_dev, NULL },
209 { 0 }
210 };
211
212 DEVICE rx_dev = {
213 "RX", rx_unit, rx_reg, rx_mod,
214 RX_NUMDR, 8, 20, 1, 8, 8,
215 NULL, NULL, &rx_reset,
216 &rx_boot, &rx_attach, NULL,
217 &rx_dib, DEV_DISABLE
218 };
219
220 /* IOT routine */
221
222 int32 rx (int32 IR, int32 AC)
223 {
224 int32 drv = ((rx_csr & RXCS_DRV)? 1: 0); /* get drive number */
225
226 switch (IR & 07) { /* decode IR<9:11> */
227
228 case 0: /* unused */
229 break;
230
231 case 1: /* LCD */
232 if (rx_state != IDLE) return AC; /* ignore if busy */
233 dev_done = dev_done & ~INT_RX; /* clear done, int */
234 int_req = int_req & ~INT_RX;
235 rx_tr = rx_err = 0; /* clear flags */
236 rx_bptr = 0; /* clear buf pointer */
237 if (rx_28 && (AC & RXCS_MODE)) { /* RX28 8b mode? */
238 rx_dbr = rx_csr = AC & 0377; /* save 8b */
239 rx_tr = 1; /* xfer is ready */
240 rx_state = CMD8; /* wait for part 2 */
241 }
242 else {
243 rx_dbr = rx_csr = AC; /* save new command */
244 rx_cmd (); /* issue command */
245 }
246 return 0; /* clear AC */
247
248 case 2: /* XDR */
249 switch (rx_state & 017) { /* case on state */
250
251 case EMPTY: /* emptying buffer */
252 sim_activate (&rx_unit[drv], rx_xwait); /* sched xfer */
253 return READ_RXDBR; /* return data reg */
254
255 case CMD8: /* waiting for cmd */
256 rx_dbr = AC & 0377;
257 rx_csr = (rx_csr & 0377) | ((AC & 017) << 8);
258 rx_cmd ();
259 break;
260
261 case RWDS:case RWDT:case FILL:case SDCNF: /* waiting for data */
262 rx_dbr = AC; /* save data */
263 sim_activate (&rx_unit[drv], rx_xwait); /* schedule */
264 break;
265
266 default: /* default */
267 return READ_RXDBR; /* return data reg */
268 }
269 break;
270
271 case 3: /* STR */
272 if (rx_tr != 0) {
273 rx_tr = 0;
274 return IOT_SKP + AC;
275 }
276 break;
277
278 case 4: /* SER */
279 if (rx_err != 0) {
280 rx_err = 0;
281 return IOT_SKP + AC;
282 }
283 break;
284
285 case 5: /* SDN */
286 if ((dev_done & INT_RX) != 0) {
287 dev_done = dev_done & ~INT_RX;
288 int_req = int_req & ~INT_RX;
289 return IOT_SKP + AC;
290 }
291 break;
292
293 case 6: /* INTR */
294 if (AC & 1) int_enable = int_enable | INT_RX;
295 else int_enable = int_enable & ~INT_RX;
296 int_req = INT_UPDATE;
297 break;
298
299 case 7: /* INIT */
300 rx_reset (&rx_dev); /* reset device */
301 break;
302 } /* end case pulse */
303
304 return AC;
305 }
306
307 void rx_cmd (void)
308 {
309 int32 drv = ((rx_csr & RXCS_DRV)? 1: 0); /* get drive number */
310
311 switch (RXCS_GETFNC (rx_csr)) { /* decode command */
312
313 case RXCS_FILL:
314 rx_state = FILL; /* state = fill */
315 rx_tr = 1; /* xfer is ready */
316 rx_esr = rx_esr & RXES_ID; /* clear errors */
317 break;
318
319 case RXCS_EMPTY:
320 rx_state = EMPTY; /* state = empty */
321 rx_esr = rx_esr & RXES_ID; /* clear errors */
322 sim_activate (&rx_unit[drv], rx_xwait); /* sched xfer */
323 break;
324
325 case RXCS_READ: case RXCS_WRITE: case RXCS_WRDEL:
326 rx_state = RWDS; /* state = get sector */
327 rx_tr = 1; /* xfer is ready */
328 rx_esr = rx_esr & RXES_ID; /* clear errors */
329 break;
330
331 case RXCS_SDEN:
332 if (rx_28) { /* RX28? */
333 rx_state = SDCNF; /* state = get conf */
334 rx_tr = 1; /* xfer is ready */
335 rx_esr = rx_esr & RXES_ID; /* clear errors */
336 break;
337 } /* else fall thru */
338 default:
339 rx_state = CMD_COMPLETE; /* state = cmd compl */
340 sim_activate (&rx_unit[drv], rx_cwait); /* sched done */
341 break;
342 } /* end switch func */
343
344 return;
345 }
346
347 /* Unit service; the action to be taken depends on the transfer state:
348
349 IDLE Should never get here
350 RWDS Save sector, set TR, set RWDT
351 RWDT Save track, set RWXFR
352 RWXFR Read/write buffer
353 FILL copy dbr to rx_buf[rx_bptr], advance ptr
354 if rx_bptr > max, finish command, else set tr
355 EMPTY if rx_bptr > max, finish command, else
356 copy rx_buf[rx_bptr] to dbr, advance ptr, set tr
357 CMD_COMPLETE copy requested data to dbr, finish command
358 INIT_COMPLETE read drive 0, track 1, sector 1 to buffer, finish command
359
360 For RWDT and CMD_COMPLETE, the input argument is the selected drive;
361 otherwise, it is drive 0.
362 */
363
364 t_stat rx_svc (UNIT *uptr)
365 {
366 int32 i, func, byptr, bps, wps;
367 int8 *fbuf = uptr->filebuf;
368 uint32 da;
369 #define PTR12(x) (((x) + (x) + (x)) >> 1)
370
371 if (rx_28 && (uptr->flags & UNIT_DEN)) /* RX28 and double density? */
372 bps = RX2_NUMBY; /* double bytes/sector */
373 else bps = RX_NUMBY; /* RX8E, normal count */
374 wps = bps / 2;
375 func = RXCS_GETFNC (rx_csr); /* get function */
376 switch (rx_state) { /* case on state */
377
378 case IDLE: /* idle */
379 return SCPE_IERR;
380
381 case EMPTY: /* empty buffer */
382 if (rx_csr & RXCS_MODE) { /* 8b xfer? */
383 if (rx_bptr >= bps) { /* done? */
384 rx_done (0, 0); /* set done */
385 break; /* and exit */
386 }
387 rx_dbr = rx_buf[rx_bptr]; /* else get data */
388 }
389 else {
390 byptr = PTR12 (rx_bptr); /* 12b xfer */
391 if (rx_bptr >= wps) { /* done? */
392 rx_done (0, 0); /* set done */
393 break; /* and exit */
394 }
395 rx_dbr = (rx_bptr & 1)? /* get data */
396 ((rx_buf[byptr] & 017) << 8) | rx_buf[byptr + 1]:
397 (rx_buf[byptr] << 4) | ((rx_buf[byptr + 1] >> 4) & 017);
398 }
399 rx_bptr = rx_bptr + 1;
400 rx_tr = 1;
401 break;
402
403 case FILL: /* fill buffer */
404 if (rx_csr & RXCS_MODE) { /* 8b xfer? */
405 rx_buf[rx_bptr] = rx_dbr; /* fill buffer */
406 rx_bptr = rx_bptr + 1;
407 if (rx_bptr < bps) rx_tr = 1; /* if more, set xfer */
408 else rx_done (0, 0); /* else done */
409 }
410 else {
411 byptr = PTR12 (rx_bptr); /* 12b xfer */
412 if (rx_bptr & 1) { /* odd or even? */
413 rx_buf[byptr] = (rx_buf[byptr] & 0360) | ((rx_dbr >> 8) & 017);
414 rx_buf[byptr + 1] = rx_dbr & 0377;
415 }
416 else {
417 rx_buf[byptr] = (rx_dbr >> 4) & 0377;
418 rx_buf[byptr + 1] = (rx_dbr & 017) << 4;
419 }
420 rx_bptr = rx_bptr + 1;
421 if (rx_bptr < wps) rx_tr = 1; /* if more, set xfer */
422 else {
423 for (i = PTR12 (wps); i < bps; i++)
424 rx_buf[i] = 0; /* else fill sector */
425 rx_done (0, 0); /* set done */
426 }
427 }
428 break;
429
430 case RWDS: /* wait for sector */
431 rx_sector = rx_dbr & RX_M_SECTOR; /* save sector */
432 rx_tr = 1; /* set xfer ready */
433 rx_state = RWDT; /* advance state */
434 return SCPE_OK;
435
436 case RWDT: /* wait for track */
437 rx_track = rx_dbr & RX_M_TRACK; /* save track */
438 rx_state = RWXFR;
439 sim_activate (uptr, /* sched done */
440 rx_swait * abs (rx_track - uptr->TRACK));
441 return SCPE_OK;
442
443 case RWXFR: /* transfer */
444 if ((uptr->flags & UNIT_BUF) == 0) { /* not buffered? */
445 rx_done (0, 0110); /* done, error */
446 return IORETURN (rx_stopioe, SCPE_UNATT);
447 }
448 if (rx_track >= RX_NUMTR) { /* bad track? */
449 rx_done (0, 0040); /* done, error */
450 break;
451 }
452 uptr->TRACK = rx_track; /* now on track */
453 if ((rx_sector == 0) || (rx_sector > RX_NUMSC)) { /* bad sect? */
454 rx_done (0, 0070); /* done, error */
455 break;
456 }
457 if (rx_28 && /* RX28? */
458 (((uptr->flags & UNIT_DEN) != 0) ^
459 ((rx_csr & RXCS_DEN) != 0))) { /* densities agree? */
460 rx_done (RXES_DERR, 0240); /* no, error */
461 break;
462 }
463 da = CALC_DA (rx_track, rx_sector, bps); /* get disk address */
464 if (func == RXCS_WRDEL) rx_esr = rx_esr | RXES_DD; /* del data? */
465 if (func == RXCS_READ) { /* read? */
466 for (i = 0; i < bps; i++) rx_buf[i] = fbuf[da + i];
467 }
468 else { /* write */
469 if (uptr->flags & UNIT_WPRT) { /* locked? */
470 rx_done (0, 0100); /* done, error */
471 break;
472 }
473 for (i = 0; i < bps; i++) fbuf[da + i] = rx_buf[i];
474 da = da + bps;
475 if (da > uptr->hwmark) uptr->hwmark = da;
476 }
477 rx_done (0, 0); /* done */
478 break;
479
480 case SDCNF: /* confirm set density */
481 if ((rx_dbr & 0377) != 0111) { /* confirmed? */
482 rx_done (0, 0250); /* no, error */
483 break;
484 }
485 rx_state = SDXFR; /* next state */
486 sim_activate (uptr, rx_cwait * 100); /* schedule operation */
487 break;
488
489 case SDXFR: /* erase disk */
490 for (i = 0; i < (int32) uptr->capac; i++) fbuf[i] = 0;
491 uptr->hwmark = uptr->capac;
492 if (rx_csr & RXCS_DEN) uptr->flags = uptr->flags | UNIT_DEN;
493 else uptr->flags = uptr->flags & ~UNIT_DEN;
494 rx_done (0, 0);
495 break;
496
497 case CMD_COMPLETE: /* command complete */
498 if (func == RXCS_ECODE) { /* read ecode? */
499 rx_dbr = rx_ecode; /* set dbr */
500 rx_done (0, -1); /* don't update */
501 }
502 else if (rx_28) { /* no, read sta; RX28? */
503 rx_esr = rx_esr & ~RXES_DERR; /* assume dens match */
504 if (((uptr->flags & UNIT_DEN) != 0) ^ /* densities mismatch? */
505 ((rx_csr & RXCS_DEN) != 0))
506 rx_done (RXES_DERR, 0240); /* yes, error */
507 else rx_done (0, 0); /* no, ok */
508 }
509 else rx_done (0, 0); /* RX8E status */
510 break;
511
512 case INIT_COMPLETE: /* init complete */
513 rx_unit[0].TRACK = 1; /* drive 0 to trk 1 */
514 rx_unit[1].TRACK = 0; /* drive 1 to trk 0 */
515 if ((rx_unit[0].flags & UNIT_BUF) == 0) { /* not buffered? */
516 rx_done (RXES_ID, 0010); /* init done, error */
517 break;
518 }
519 da = CALC_DA (1, 1, bps); /* track 1, sector 1 */
520 for (i = 0; i < bps; i++) /* read sector */
521 rx_buf[i] = fbuf[da + i];
522 rx_done (RXES_ID, 0); /* set done */
523 if ((rx_unit[1].flags & UNIT_ATT) == 0) rx_ecode = 0020;
524 break;
525 } /* end case state */
526
527 return SCPE_OK;
528 }
529
530 /* Command complete. Set done and put final value in interface register,
531 return to IDLE state.
532 */
533
534 void rx_done (int32 esr_flags, int32 new_ecode)
535 {
536 int32 drv = (rx_csr & RXCS_DRV)? 1: 0;
537
538 rx_state = IDLE; /* now idle */
539 dev_done = dev_done | INT_RX; /* set done */
540 int_req = INT_UPDATE; /* update ints */
541 rx_esr = (rx_esr | esr_flags) & ~(RXES_DRDY|RXES_RX02|RXES_DEN);
542 if (rx_28) rx_esr = rx_esr | RXES_RX02; /* RX28? */
543 if (rx_unit[drv].flags & UNIT_ATT) { /* update drv rdy */
544 rx_esr = rx_esr | RXES_DRDY;
545 if (rx_unit[drv].flags & UNIT_DEN) /* update density */
546 rx_esr = rx_esr | RXES_DEN;
547 }
548 if (new_ecode > 0) rx_err = 1; /* test for error */
549 if (new_ecode < 0) return; /* don't update? */
550 rx_ecode = new_ecode; /* update ecode */
551 rx_dbr = rx_esr; /* update RXDB */
552 return;
553 }
554
555 /* Reset routine. The RX is one of the few devices that schedules
556 an I/O transfer as part of its initialization */
557
558 t_stat rx_reset (DEVICE *dptr)
559 {
560 rx_dbr = rx_csr = 0; /* 12b mode, drive 0 */
561 rx_esr = rx_ecode = 0; /* clear error */
562 rx_tr = rx_err = 0; /* clear flags */
563 rx_track = rx_sector = 0; /* clear address */
564 rx_state = IDLE; /* ctrl idle */
565 dev_done = dev_done & ~INT_RX; /* clear done, int */
566 int_req = int_req & ~INT_RX;
567 int_enable = int_enable & ~INT_RX;
568 sim_cancel (&rx_unit[1]); /* cancel drive 1 */
569 if (dptr->flags & DEV_DIS) sim_cancel (&rx_unit[0]); /* disabled? */
570 else if (rx_unit[0].flags & UNIT_BUF) { /* attached? */
571 rx_state = INIT_COMPLETE; /* yes, sched init */
572 sim_activate (&rx_unit[0], rx_swait * abs (1 - rx_unit[0].TRACK));
573 }
574 else rx_done (rx_esr | RXES_ID, 0010); /* no, error */
575 return SCPE_OK;
576 }
577
578 /* Attach routine */
579
580 t_stat rx_attach (UNIT *uptr, char *cptr)
581 {
582 uint32 sz;
583
584 if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) {
585 if (sz > RX_SIZE) uptr->flags = uptr->flags | UNIT_DEN;
586 else uptr->flags = uptr->flags & ~UNIT_DEN;
587 }
588 uptr->capac = (uptr->flags & UNIT_DEN)? RX2_SIZE: RX_SIZE;
589 return attach_unit (uptr, cptr);
590 }
591
592 /* Set size routine */
593
594 t_stat rx_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)
595 {
596 if (uptr->flags & UNIT_ATT) return SCPE_ALATT;
597 if ((rx_28 == 0) && val) return SCPE_NOFNC; /* not on RX8E */
598 uptr->capac = val? RX2_SIZE: RX_SIZE;
599 return SCPE_OK;
600 }
601
602 /* Set controller type */
603
604 t_stat rx_settype (UNIT *uptr, int32 val, char *cptr, void *desc)
605 {
606 int32 i;
607
608 if ((val < 0) || (val > 1) || (cptr != NULL)) return SCPE_ARG;
609 if (val == rx_28) return SCPE_OK;
610 for (i = 0; i < RX_NUMDR; i++) {
611 if (rx_unit[i].flags & UNIT_ATT) return SCPE_ALATT;
612 }
613 for (i = 0; i < RX_NUMDR; i++) {
614 if (val) rx_unit[i].flags = rx_unit[i].flags | UNIT_DEN | UNIT_AUTO;
615 else rx_unit[i].flags = rx_unit[i].flags & ~(UNIT_DEN | UNIT_AUTO);
616 rx_unit[i].capac = val? RX2_SIZE: RX_SIZE;
617 }
618 rx_28 = val;
619 return SCPE_OK;
620 }
621
622 /* Show controller type */
623
624 t_stat rx_showtype (FILE *st, UNIT *uptr, int32 val, void *desc)
625 {
626 if (rx_28) fprintf (st, "RX28");
627 else fprintf (st, "RX8E");
628 return SCPE_OK;
629 }
630
631 /* Bootstrap routine */
632
633 #define BOOT_START 022
634 #define BOOT_ENTRY 022
635 #define BOOT_INST 060
636 #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16))
637 #define BOOT2_START 020
638 #define BOOT2_ENTRY 033
639 #define BOOT2_LEN (sizeof (boot2_rom) / sizeof (int16))
640
641 static const uint16 boot_rom[] = {
642 06755, /* 22, SDN */
643 05022, /* 23, JMP .-1 */
644 07126, /* 24, CLL CML RTL ; read command + */
645 01060, /* 25, TAD UNIT ; unit no */
646 06751, /* 26, LCD ; load read+unit */
647 07201, /* 27, CLA IAC ; AC = 1 */
648 04053, /* 30, JMS LOAD ; load sector */
649 04053, /* 31, JMS LOAD ; load track */
650 07104, /* 32, CLL RAL ; AC = 2 */
651 06755, /* 33, SDN */
652 05054, /* 34, JMP LOAD+1 */
653 06754, /* 35, SER */
654 07450, /* 36, SNA ; more to do? */
655 07610, /* 37, CLA SKP ; error */
656 05046, /* 40, JMP 46 ; go empty */
657 07402, /* 41-45, HALT ; error */
658 07402,
659 07402,
660 07402,
661 07402,
662 06751, /* 46, LCD ; load empty */
663 04053, /* 47, JMS LOAD ; get data */
664 03002, /* 50, DCA 2 ; store */
665 02050, /* 51, ISZ 50 ; incr store */
666 05047, /* 52, JMP 47 ; loop until done */
667 00000, /* LOAD, 0 */
668 06753, /* 54, STR */
669 05033, /* 55, JMP 33 */
670 06752, /* 56, XDR */
671 05453, /* 57, JMP I LOAD */
672 07024, /* UNIT, CML RAL ; for unit 1 */
673 06030 /* 61, KCC */
674 };
675
676 static const uint16 boot2_rom[] = {
677 01061, /* READ, TAD UNIT ; next unit+den */
678 01046, /* 21, TAD CON360 ; add in 360 */
679 00060, /* 22, AND CON420 ; mask to 420 */
680 03061, /* 23, DCA UNIT ; 400,420,0,20... */
681 07327, /* 24, STL CLA IAC RTL ; AC = 6 = read */
682 01061, /* 25, TAD UNIT ; +unit+den */
683 06751, /* 26, LCD ; load cmd */
684 07201, /* 27, CLA IAC; ; AC = 1 = trksec */
685 04053, /* 30, JMS LOAD ; load trk */
686 04053, /* 31, JMS LOAD ; load sec */
687 07004, /* CN7004, RAL ; AC = 2 = empty */
688 06755, /* START, SDN ; done? */
689 05054, /* 34, JMP LOAD+1 ; check xfr */
690 06754, /* 35, SER ; error? */
691 07450, /* 36, SNA ; AC=0 on start */
692 05020, /* 37, JMP RD ; try next den,un */
693 01061, /* 40, TAD UNIT ; +unit+den */
694 06751, /* 41, LCD ; load cmd */
695 01061, /* 42, TAD UNIT ; set 60 for sec boot */
696 00046, /* 43, AND CON360 ; only density */
697 01032, /* 44, TAD CN7004 ; magic */
698 03060, /* 45, DCA 60 */
699 00360, /* CON360, 360 ; NOP */
700 04053, /* 47, JMS LOAD ; get data */
701 03002, /* 50, DCA 2 ; store */
702 02050, /* 51, ISZ .-1 ; incr store */
703 05047, /* 52, JMP .-3 ; loop until done */
704 00000, /* LOAD, 0 */
705 06753, /* 54, STR ; xfr ready? */
706 05033, /* 55, JMP 33 ; no, chk done */
707 06752, /* 56, XDR ; get word */
708 05453, /* 57, JMP I 53 ; return */
709 00420, /* CON420, 420 ; toggle */
710 00020 /* UNIT, 20 ; unit+density */
711 };
712
713 t_stat rx_boot (int32 unitno, DEVICE *dptr)
714 {
715 int32 i;
716 extern int32 saved_PC;
717 extern uint16 M[];
718
719 if (rx_dib.dev != DEV_RX) return STOP_NOTSTD; /* only std devno */
720 if (rx_28) {
721 for (i = 0; i < BOOT2_LEN; i++) M[BOOT2_START + i] = boot2_rom[i];
722 saved_PC = BOOT2_ENTRY;
723 }
724 else {
725 for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i];
726 M[BOOT_INST] = unitno? 07024: 07004;
727 saved_PC = BOOT_ENTRY;
728 }
729 return SCPE_OK;
730 }