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