First Commit of my working state
[simh.git] / PDP11 / pdp11_rx.c
1 /* pdp11_rx.c: RX11/RX01 floppy disk simulator
2
3 Copyright (c) 1993-2005, Robert M Supnik
4
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 Except as contained in this notice, the name of Robert M Supnik shall not be
23 used in advertising or otherwise to promote the sale, use or other dealings
24 in this Software without prior written authorization from Robert M Supnik.
25
26 rx RX11/RX01 floppy disk
27
28 07-Jul-05 RMS Removed extraneous externs
29 12-Oct-02 RMS Added autoconfigure support
30 08-Oct-02 RMS Added variable address support to bootstrap
31 Added vector change/display support
32 Revised state machine based on RX211
33 New data structures
34 Fixed reset of disabled device
35 26-Jan-02 RMS Revised bootstrap to conform to M9312
36 06-Jan-02 RMS Revised enable/disable support
37 30-Nov-01 RMS Added read only unit, extended SET/SHOW support
38 24-Nov-01 RMS Converted FLG to array
39 07-Sep-01 RMS Revised device disable and interrupt mechanisms
40 17-Jul-01 RMS Fixed warning from VC++ 6.0
41 26-Apr-01 RMS Added device enable/disable support
42 13-Apr-01 RMS Revised for register arrays
43 15-Feb-01 RMS Corrected bootstrap string
44 14-Apr-99 RMS Changed t_addr to unsigned
45
46 An RX01 diskette consists of 77 tracks, each with 26 sectors of 128B.
47 Tracks are numbered 0-76, sectors 1-26.
48 */
49
50 #include "pdp11_defs.h"
51
52 #define RX_NUMTR 77 /* tracks/disk */
53 #define RX_M_TRACK 0377
54 #define RX_NUMSC 26 /* sectors/track */
55 #define RX_M_SECTOR 0177
56 #define RX_NUMBY 128 /* bytes/sector */
57 #define RX_SIZE (RX_NUMTR * RX_NUMSC * RX_NUMBY) /* bytes/disk */
58 #define RX_NUMDR 2 /* drives/controller */
59 #define RX_M_NUMDR 01
60 #define UNIT_V_WLK (UNIT_V_UF) /* write locked */
61 #define UNIT_WLK (1u << UNIT_V_UF)
62 #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */
63
64 #define IDLE 0 /* idle state */
65 #define RWDS 1 /* rw, sect next */
66 #define RWDT 2 /* rw, track next */
67 #define RWXFR 3 /* rw, transfer */
68 #define FILL 4 /* fill buffer */
69 #define EMPTY 5 /* empty buffer */
70 #define CMD_COMPLETE 6 /* set done next */
71 #define INIT_COMPLETE 7 /* init compl next */
72
73 #define RXCS_V_FUNC 1 /* function */
74 #define RXCS_M_FUNC 7
75 #define RXCS_FILL 0 /* fill buffer */
76 #define RXCS_EMPTY 1 /* empty buffer */
77 #define RXCS_WRITE 2 /* write sector */
78 #define RXCS_READ 3 /* read sector */
79 #define RXCS_RXES 5 /* read status */
80 #define RXCS_WRDEL 6 /* write del data */
81 #define RXCS_ECODE 7 /* read error code */
82 #define RXCS_V_DRV 4 /* drive select */
83 #define RXCS_V_DONE 5 /* done */
84 #define RXCS_V_IE 6 /* intr enable */
85 #define RXCS_V_TR 7 /* xfer request */
86 #define RXCS_V_INIT 14 /* init */
87 #define RXCS_V_ERR 15 /* error */
88 #define RXCS_FUNC (RXCS_M_FUNC << RXCS_V_FUNC)
89 #define RXCS_DRV (1u << RXCS_V_DRV)
90 #define RXCS_DONE (1u << RXCS_V_DONE)
91 #define RXCS_IE (1u << RXCS_V_IE)
92 #define RXCS_TR (1u << RXCS_V_TR)
93 #define RXCS_INIT (1u << RXCS_V_INIT)
94 #define RXCS_ERR (1u << RXCS_V_ERR)
95 #define RXCS_ROUT (RXCS_ERR+RXCS_TR+RXCS_IE+RXCS_DONE)
96 #define RXCS_IMP (RXCS_ROUT+RXCS_DRV+RXCS_FUNC)
97 #define RXCS_RW (RXCS_IE) /* read/write */
98 #define RXCS_GETFNC(x) (((x) >> RXCS_V_FUNC) & RXCS_M_FUNC)
99
100 #define RXES_CRC 0001 /* CRC error */
101 #define RXES_PAR 0002 /* parity error */
102 #define RXES_ID 0004 /* init done */
103 #define RXES_WLK 0010 /* write protect */
104 #define RXES_DD 0100 /* deleted data */
105 #define RXES_DRDY 0200 /* drive ready */
106
107 #define TRACK u3 /* current track */
108 #define CALC_DA(t,s) (((t) * RX_NUMSC) + ((s) - 1)) * RX_NUMBY
109
110 extern int32 int_req[IPL_HLVL];
111
112 int32 rx_csr = 0; /* control/status */
113 int32 rx_dbr = 0; /* data buffer */
114 int32 rx_esr = 0; /* error status */
115 int32 rx_ecode = 0; /* error code */
116 int32 rx_track = 0; /* desired track */
117 int32 rx_sector = 0; /* desired sector */
118 int32 rx_state = IDLE; /* controller state */
119 int32 rx_stopioe = 1; /* stop on error */
120 int32 rx_cwait = 100; /* command time */
121 int32 rx_swait = 10; /* seek, per track */
122 int32 rx_xwait = 1; /* tr set time */
123 uint8 rx_buf[RX_NUMBY] = { 0 }; /* sector buffer */
124 int32 rx_bptr = 0; /* buffer pointer */
125 int32 rx_enb = 1; /* device enable */
126
127 DEVICE rx_dev;
128 t_stat rx_rd (int32 *data, int32 PA, int32 access);
129 t_stat rx_wr (int32 data, int32 PA, int32 access);
130 t_stat rx_svc (UNIT *uptr);
131 t_stat rx_reset (DEVICE *dptr);
132 t_stat rx_boot (int32 unitno, DEVICE *dptr);
133 void rx_done (int esr_flags, int new_ecode);
134
135 /* RX11 data structures
136
137 rx_dev RX device descriptor
138 rx_unit RX unit list
139 rx_reg RX register list
140 rx_mod RX modifier list
141 */
142
143 DIB rx_dib = {
144 IOBA_RX, IOLN_RX, &rx_rd, &rx_wr,
145 1, IVCL (RX), VEC_RX, { NULL }
146 };
147
148 UNIT rx_unit[] = {
149 { UDATA (&rx_svc,
150 UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, RX_SIZE) },
151 { UDATA (&rx_svc,
152 UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, RX_SIZE) }
153 };
154
155 REG rx_reg[] = {
156 { ORDATA (RXCS, rx_csr, 16) },
157 { ORDATA (RXDB, rx_dbr, 8) },
158 { ORDATA (RXES, rx_esr, 8) },
159 { ORDATA (RXERR, rx_ecode, 8) },
160 { ORDATA (RXTA, rx_track, 8) },
161 { ORDATA (RXSA, rx_sector, 8) },
162 { DRDATA (STAPTR, rx_state, 3), REG_RO },
163 { DRDATA (BUFPTR, rx_bptr, 7) },
164 { FLDATA (INT, IREQ (RX), INT_V_RX) },
165 { FLDATA (ERR, rx_csr, RXCS_V_ERR) },
166 { FLDATA (TR, rx_csr, RXCS_V_TR) },
167 { FLDATA (IE, rx_csr, RXCS_V_IE) },
168 { FLDATA (DONE, rx_csr, RXCS_V_DONE) },
169 { DRDATA (CTIME, rx_cwait, 24), PV_LEFT },
170 { DRDATA (STIME, rx_swait, 24), PV_LEFT },
171 { DRDATA (XTIME, rx_xwait, 24), PV_LEFT },
172 { FLDATA (STOP_IOE, rx_stopioe, 0) },
173 { BRDATA (SBUF, rx_buf, 8, 8, RX_NUMBY) },
174 { ORDATA (DEVADDR, rx_dib.ba, 32), REG_HRO },
175 { ORDATA (DEVVEC, rx_dib.vec, 16), REG_HRO },
176 { NULL }
177 };
178
179 MTAB rx_mod[] = {
180 { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },
181 { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },
182 { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS",
183 &set_addr, &show_addr, NULL },
184 { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR",
185 &set_vec, &show_vec, NULL },
186 { MTAB_XTD | MTAB_VDV, 0, NULL, "AUTOCONFIGURE",
187 &set_addr_flt, NULL, NULL },
188 { 0 }
189 };
190
191 DEVICE rx_dev = {
192 "RX", rx_unit, rx_reg, rx_mod,
193 RX_NUMDR, 8, 20, 1, 8, 8,
194 NULL, NULL, &rx_reset,
195 &rx_boot, NULL, NULL,
196 &rx_dib, DEV_FLTA | DEV_DISABLE | DEV_UBUS | DEV_QBUS
197 };
198
199 /* I/O dispatch routine, I/O addresses 17777170 - 17777172
200
201 17777170 floppy CSR
202 17777172 floppy data register
203 */
204
205 t_stat rx_rd (int32 *data, int32 PA, int32 access)
206 {
207 switch ((PA >> 1) & 1) { /* decode PA<1> */
208
209 case 0: /* RXCS */
210 rx_csr = rx_csr & RXCS_IMP; /* clear junk */
211 *data = rx_csr & RXCS_ROUT;
212 break;
213
214 case 1: /* RXDB */
215 if ((rx_state == EMPTY) && (rx_csr & RXCS_TR)) {/* empty? */
216 sim_activate (&rx_unit[0], rx_xwait);
217 rx_csr = rx_csr & ~RXCS_TR; /* clear xfer */
218 }
219 *data = rx_dbr; /* return data */
220 break;
221 } /* end switch PA */
222
223 return SCPE_OK;
224 }
225
226 t_stat rx_wr (int32 data, int32 PA, int32 access)
227 {
228 int32 drv;
229
230 switch ((PA >> 1) & 1) { /* decode PA<1> */
231
232 /* Writing RXCS, three cases:
233 1. Writing INIT, reset device
234 2. Idle and writing new function
235 - clear error, done, transfer ready, int req
236 - save int enable, function, drive
237 - start new function
238 3. Otherwise, write IE and update interrupts
239 */
240
241 case 0: /* RXCS */
242 rx_csr = rx_csr & RXCS_IMP; /* clear junk */
243 if (access == WRITEB) data = (PA & 1)? /* write byte? */
244 (rx_csr & 0377) | (data << 8): (rx_csr & ~0377) | data;
245 if (data & RXCS_INIT) { /* initialize? */
246 rx_reset (&rx_dev); /* reset device */
247 return SCPE_OK; /* end if init */
248 }
249 if ((data & CSR_GO) && (rx_state == IDLE)) { /* new function? */
250 rx_csr = data & (RXCS_IE + RXCS_DRV + RXCS_FUNC);
251 drv = ((rx_csr & RXCS_DRV)? 1: 0); /* reselect drive */
252 rx_bptr = 0; /* clear buf pointer */
253 switch (RXCS_GETFNC (data)) { /* case on func */
254
255 case RXCS_FILL:
256 rx_state = FILL; /* state = fill */
257 rx_csr = rx_csr | RXCS_TR; /* xfer is ready */
258 break;
259
260 case RXCS_EMPTY:
261 rx_state = EMPTY; /* state = empty */
262 sim_activate (&rx_unit[drv], rx_xwait);
263 break;
264
265 case RXCS_READ: case RXCS_WRITE: case RXCS_WRDEL:
266 rx_state = RWDS; /* state = get sector */
267 rx_csr = rx_csr | RXCS_TR; /* xfer is ready */
268 rx_esr = rx_esr & RXES_ID; /* clear errors */
269 break;
270
271 default:
272 rx_state = CMD_COMPLETE; /* state = cmd compl */
273 sim_activate (&rx_unit[drv], rx_cwait);
274 break;
275 } /* end switch func */
276 return SCPE_OK;
277 } /* end if GO */
278 if ((data & RXCS_IE) == 0) CLR_INT (RX);
279 else if ((rx_csr & (RXCS_DONE + RXCS_IE)) == RXCS_DONE)
280 SET_INT (RX);
281 rx_csr = (rx_csr & ~RXCS_RW) | (data & RXCS_RW);
282 break; /* end case RXCS */
283
284 /* Accessing RXDB, two cases:
285 1. Write idle, write
286 2. Write not idle and TR set, state dependent
287 */
288
289 case 1: /* RXDB */
290 if ((PA & 1) || ((rx_state != IDLE) && ((rx_csr & RXCS_TR) == 0)))
291 return SCPE_OK; /* if ~IDLE, need tr */
292 rx_dbr = data & 0377; /* save data */
293 if ((rx_state != IDLE) && (rx_state != EMPTY)) {
294 drv = ((rx_csr & RXCS_DRV)? 1: 0); /* select drive */
295 sim_activate (&rx_unit[drv], rx_xwait); /* sched event */
296 rx_csr = rx_csr & ~RXCS_TR; /* clear xfer */
297 }
298 break; /* end case RXDB */
299 } /* end switch PA */
300
301 return SCPE_OK;
302 }
303
304 /* Unit service; the action to be taken depends on the transfer state:
305
306 IDLE Should never get here
307 RWDS Save sector, set TR, set RWDT
308 RWDT Save track, set RWXFR
309 RWXFR Read/write buffer
310 FILL copy ir to rx_buf[rx_bptr], advance ptr
311 if rx_bptr > max, finish command, else set tr
312 EMPTY if rx_bptr > max, finish command, else
313 copy rx_buf[rx_bptr] to ir, advance ptr, set tr
314 CMD_COMPLETE copy requested data to ir, finish command
315 INIT_COMPLETE read drive 0, track 1, sector 1 to buffer, finish command
316
317 For RWDT and CMD_COMPLETE, the input argument is the selected drive;
318 otherwise, it is drive 0.
319 */
320
321 t_stat rx_svc (UNIT *uptr)
322 {
323 int32 i, func;
324 uint32 da;
325 int8 *fbuf = uptr->filebuf;
326
327 func = RXCS_GETFNC (rx_csr); /* get function */
328 switch (rx_state) { /* case on state */
329
330 case IDLE: /* idle */
331 return SCPE_IERR; /* done */
332
333 case EMPTY: /* empty buffer */
334 if (rx_bptr >= RX_NUMBY) rx_done (0, 0); /* done all? */
335 else {
336 rx_dbr = rx_buf[rx_bptr]; /* get next */
337 rx_bptr = rx_bptr + 1;
338 rx_csr = rx_csr | RXCS_TR; /* set xfer */
339 }
340 break;
341
342 case FILL: /* fill buffer */
343 rx_buf[rx_bptr] = rx_dbr; /* write next */
344 rx_bptr = rx_bptr + 1;
345 if (rx_bptr < RX_NUMBY) rx_csr = rx_csr | RXCS_TR; /* more? set xfer */
346 else rx_done (0, 0); /* else done */
347 break;
348
349 case RWDS: /* wait for sector */
350 rx_sector = rx_dbr & RX_M_SECTOR; /* save sector */
351 rx_csr = rx_csr | RXCS_TR; /* set xfer */
352 rx_state = RWDT; /* advance state */
353 return SCPE_OK;
354
355 case RWDT: /* wait for track */
356 rx_track = rx_dbr & RX_M_TRACK; /* save track */
357 rx_state = RWXFR;
358 sim_activate (uptr, /* sched done */
359 rx_swait * abs (rx_track - uptr->TRACK));
360 return SCPE_OK;
361
362 case RWXFR:
363 if ((uptr->flags & UNIT_BUF) == 0) { /* not buffered? */
364 rx_done (0, 0110); /* done, error */
365 return IORETURN (rx_stopioe, SCPE_UNATT);
366 }
367 if (rx_track >= RX_NUMTR) { /* bad track? */
368 rx_done (0, 0040); /* done, error */
369 break;
370 }
371 uptr->TRACK = rx_track; /* now on track */
372 if ((rx_sector == 0) || (rx_sector > RX_NUMSC)) { /* bad sect? */
373 rx_done (0, 0070); /* done, error */
374 break;
375 }
376 da = CALC_DA (rx_track, rx_sector); /* get disk address */
377 if (func == RXCS_WRDEL) rx_esr = rx_esr | RXES_DD; /* del data? */
378 if (func == RXCS_READ) { /* read? */
379 for (i = 0; i < RX_NUMBY; i++)
380 rx_buf[i] = fbuf[da + i];
381 }
382 else {
383 if (uptr->flags & UNIT_WPRT) { /* write and locked? */
384 rx_done (RXES_WLK, 0100); /* done, error */
385 break;
386 }
387 for (i = 0; i < RX_NUMBY; i++) /* write */
388 fbuf[da + i] = rx_buf[i];
389 da = da + RX_NUMBY;
390 if (da > uptr->hwmark) uptr->hwmark = da;
391 }
392 rx_done (0, 0); /* done */
393 break;
394
395 case CMD_COMPLETE: /* command complete */
396 if (func == RXCS_ECODE) { /* read ecode? */
397 rx_dbr = rx_ecode; /* set dbr */
398 rx_done (0, -1); /* don't update */
399 }
400 else rx_done (0, 0);
401 break;
402
403 case INIT_COMPLETE: /* init complete */
404 rx_unit[0].TRACK = 1; /* drive 0 to trk 1 */
405 rx_unit[1].TRACK = 0; /* drive 1 to trk 0 */
406 if ((rx_unit[0].flags & UNIT_BUF) == 0) { /* not buffered? */
407 rx_done (RXES_ID, 0010); /* init done, error */
408 break;
409 }
410 da = CALC_DA (1, 1); /* track 1, sector 1 */
411 for (i = 0; i < RX_NUMBY; i++) /* read sector */
412 rx_buf[i] = fbuf[da + i];
413 rx_done (RXES_ID, 0); /* set done */
414 if ((rx_unit[1].flags & UNIT_ATT) == 0) rx_ecode = 0020;
415 break;
416 } /* end case state */
417
418 return SCPE_OK;
419 }
420
421 /* Command complete. Set done and put final value in interface register,
422 request interrupt if needed, return to IDLE state.
423 */
424
425 void rx_done (int32 esr_flags, int32 new_ecode)
426 {
427 int32 drv = (rx_csr & RXCS_DRV)? 1: 0;
428
429 rx_state = IDLE; /* now idle */
430 rx_csr = rx_csr | RXCS_DONE; /* set done */
431 if (rx_csr & RXCS_IE) SET_INT (RX); /* if ie, intr */
432 rx_esr = (rx_esr | esr_flags) & ~RXES_DRDY;
433 if (rx_unit[drv].flags & UNIT_ATT)
434 rx_esr = rx_esr | RXES_DRDY;
435 if (new_ecode > 0) rx_csr = rx_csr | RXCS_ERR; /* test for error */
436 if (new_ecode < 0) return; /* don't update? */
437 rx_ecode = new_ecode; /* update ecode */
438 rx_dbr = rx_esr; /* update RXDB */
439 return;
440 }
441
442 /* Device initialization. The RX is one of the few devices that schedules
443 an I/O transfer as part of its initialization.
444 */
445
446 t_stat rx_reset (DEVICE *dptr)
447 {
448 rx_csr = rx_dbr = 0; /* clear regs */
449 rx_esr = rx_ecode = 0; /* clear error */
450 rx_track = rx_sector = 0; /* clear addr */
451 rx_state = IDLE; /* ctrl idle */
452 CLR_INT (RX); /* clear int req */
453 sim_cancel (&rx_unit[1]); /* cancel drive 1 */
454 if (dptr->flags & DEV_DIS) sim_cancel (&rx_unit[0]); /* disabled? */
455 else if (rx_unit[0].flags & UNIT_BUF) { /* attached? */
456 rx_state = INIT_COMPLETE; /* yes, sched init */
457 sim_activate (&rx_unit[0], rx_swait * abs (1 - rx_unit[0].TRACK));
458 }
459 else rx_done (0, 0010); /* no, error */
460 return auto_config (0, 0); /* run autoconfig */
461 }
462
463 /* Device bootstrap */
464
465 #define BOOT_START 02000 /* start */
466 #define BOOT_ENTRY (BOOT_START + 002) /* entry */
467 #define BOOT_UNIT (BOOT_START + 010) /* unit number */
468 #define BOOT_CSR (BOOT_START + 026) /* CSR */
469 #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16))
470
471 static const uint16 boot_rom[] = {
472 042130, /* "XD" */
473 0012706, BOOT_START, /* MOV #boot_start, SP */
474 0012700, 0000000, /* MOV #unit, R0 ; unit number */
475 0010003, /* MOV R0, R3 */
476 0006303, /* ASL R3 */
477 0006303, /* ASL R3 */
478 0006303, /* ASL R3 */
479 0006303, /* ASL R3 */
480 0012701, 0177170, /* MOV #RXCS, R1 ; csr */
481 0032711, 0000040, /* BITB #40, (R1) ; ready? */
482 0001775, /* BEQ .-4 */
483 0052703, 0000007, /* BIS #READ+GO, R3 */
484 0010311, /* MOV R3, (R1) ; read & go */
485 0105711, /* TSTB (R1) ; xfr ready? */
486 0100376, /* BPL .-2 */
487 0012761, 0000001, 0000002, /* MOV #1, 2(R1) ; sector */
488 0105711, /* TSTB (R1) ; xfr ready? */
489 0100376, /* BPL .-2 */
490 0012761, 0000001, 0000002, /* MOV #1, 2(R1) ; track */
491 0005003, /* CLR R3 */
492 0032711, 0000040, /* BITB #40, (R1) ; ready? */
493 0001775, /* BEQ .-4 */
494 0012711, 0000003, /* MOV #EMPTY+GO, (R1) ; empty & go */
495 0105711, /* TSTB (R1) ; xfr, done? */
496 0001776, /* BEQ .-2 */
497 0100003, /* BPL .+010 */
498 0116123, 0000002, /* MOVB 2(R1), (R3)+ ; move byte */
499 0000772, /* BR .-012 */
500 0005002, /* CLR R2 */
501 0005003, /* CLR R3 */
502 0012704, BOOT_START+020, /* MOV #START+20, R4 */
503 0005005, /* CLR R5 */
504 0005007 /* CLR R7 */
505 };
506
507 t_stat rx_boot (int32 unitno, DEVICE *dptr)
508 {
509 int32 i;
510 extern int32 saved_PC;
511 extern uint16 *M;
512
513 for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i];
514 M[BOOT_UNIT >> 1] = unitno & RX_M_NUMDR;
515 M[BOOT_CSR >> 1] = rx_dib.ba & DMASK;
516 saved_PC = BOOT_ENTRY;
517 return SCPE_OK;
518 }