1 /* pdp11_rx.c: RX11/RX01 floppy disk simulator
2
3 Copyright (c) 1993-2008, 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 (int32 esr_flags, int32 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 #if defined (VM_PDP11)
183 { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS",
184 &set_addr, &show_addr, NULL },
185 { MTAB_XTD | MTAB_VDV, 0, NULL, "AUTOCONFIGURE",
186 &set_addr_flt, NULL, NULL },
187 { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR",
188 &set_vec, &show_vec, NULL },
189 #else
190 { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS",
191 NULL, &show_addr, NULL },
192 { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR",
193 NULL, &show_vec, NULL },
194 #endif
195 { 0 }
196 };
197
198 DEVICE rx_dev = {
199 "RX", rx_unit, rx_reg, rx_mod,
200 RX_NUMDR, 8, 20, 1, 8, 8,
201 NULL, NULL, &rx_reset,
202 &rx_boot, NULL, NULL,
203 &rx_dib, DEV_FLTA | DEV_DISABLE | DEV_UBUS | DEV_QBUS
204 };
205
206 /* I/O dispatch routine, I/O addresses 17777170 - 17777172
207
208 17777170 floppy CSR
209 17777172 floppy data register
210 */
211
rx_rd(int32 * data,int32 PA,int32 access)212 t_stat rx_rd (int32 *data, int32 PA, int32 access)
213 {
214 switch ((PA >> 1) & 1) { /* decode PA<1> */
215
216 case 0: /* RXCS */
217 rx_csr = rx_csr & RXCS_IMP; /* clear junk */
218 *data = rx_csr & RXCS_ROUT;
219 break;
220
221 case 1: /* RXDB */
222 if ((rx_state == EMPTY) && (rx_csr & RXCS_TR)) {/* empty? */
223 sim_activate (&rx_unit[0], rx_xwait);
224 rx_csr = rx_csr & ~RXCS_TR; /* clear xfer */
225 }
226 *data = rx_dbr; /* return data */
227 break;
228 } /* end switch PA */
229
230 return SCPE_OK;
231 }
232
rx_wr(int32 data,int32 PA,int32 access)233 t_stat rx_wr (int32 data, int32 PA, int32 access)
234 {
235 int32 drv;
236
237 switch ((PA >> 1) & 1) { /* decode PA<1> */
238
239 /* Writing RXCS, three cases:
240 1. Writing INIT, reset device
241 2. Idle and writing new function
242 - clear error, done, transfer ready, int req
243 - save int enable, function, drive
244 - start new function
245 3. Otherwise, write IE and update interrupts
246 */
247
248 case 0: /* RXCS */
249 rx_csr = rx_csr & RXCS_IMP; /* clear junk */
250 if (access == WRITEB) data = (PA & 1)? /* write byte? */
251 (rx_csr & 0377) | (data << 8): (rx_csr & ~0377) | data;
252 if (data & RXCS_INIT) { /* initialize? */
253 rx_reset (&rx_dev); /* reset device */
254 return SCPE_OK; /* end if init */
255 }
256 if ((data & CSR_GO) && (rx_state == IDLE)) { /* new function? */
257 rx_csr = data & (RXCS_IE + RXCS_DRV + RXCS_FUNC);
258 drv = ((rx_csr & RXCS_DRV)? 1: 0); /* reselect drive */
259 rx_bptr = 0; /* clear buf pointer */
260 switch (RXCS_GETFNC (data)) { /* case on func */
261
262 case RXCS_FILL:
263 rx_state = FILL; /* state = fill */
264 rx_csr = rx_csr | RXCS_TR; /* xfer is ready */
265 break;
266
267 case RXCS_EMPTY:
268 rx_state = EMPTY; /* state = empty */
269 sim_activate (&rx_unit[drv], rx_xwait);
270 break;
271
272 case RXCS_READ: case RXCS_WRITE: case RXCS_WRDEL:
273 rx_state = RWDS; /* state = get sector */
274 rx_csr = rx_csr | RXCS_TR; /* xfer is ready */
275 rx_esr = rx_esr & RXES_ID; /* clear errors */
276 break;
277
278 default:
279 rx_state = CMD_COMPLETE; /* state = cmd compl */
280 sim_activate (&rx_unit[drv], rx_cwait);
281 break;
282 } /* end switch func */
283 return SCPE_OK;
284 } /* end if GO */
285 if ((data & RXCS_IE) == 0)
286 CLR_INT (RX);
287 else if ((rx_csr & (RXCS_DONE + RXCS_IE)) == RXCS_DONE)
288 SET_INT (RX);
289 rx_csr = (rx_csr & ~RXCS_RW) | (data & RXCS_RW);
290 break; /* end case RXCS */
291
292 /* Accessing RXDB, two cases:
293 1. Write idle, write
294 2. Write not idle and TR set, state dependent
295 */
296
297 case 1: /* RXDB */
298 if ((PA & 1) || ((rx_state != IDLE) && ((rx_csr & RXCS_TR) == 0)))
299 return SCPE_OK; /* if ~IDLE, need tr */
300 rx_dbr = data & 0377; /* save data */
301 if ((rx_state != IDLE) && (rx_state != EMPTY)) {
302 drv = ((rx_csr & RXCS_DRV)? 1: 0); /* select drive */
303 sim_activate (&rx_unit[drv], rx_xwait); /* sched event */
304 rx_csr = rx_csr & ~RXCS_TR; /* clear xfer */
305 }
306 break; /* end case RXDB */
307 } /* end switch PA */
308
309 return SCPE_OK;
310 }
311
312 /* Unit service; the action to be taken depends on the transfer state:
313
314 IDLE Should never get here
315 RWDS Save sector, set TR, set RWDT
316 RWDT Save track, set RWXFR
317 RWXFR Read/write buffer
318 FILL copy ir to rx_buf[rx_bptr], advance ptr
319 if rx_bptr > max, finish command, else set tr
320 EMPTY if rx_bptr > max, finish command, else
321 copy rx_buf[rx_bptr] to ir, advance ptr, set tr
322 CMD_COMPLETE copy requested data to ir, finish command
323 INIT_COMPLETE read drive 0, track 1, sector 1 to buffer, finish command
324
325 For RWDT and CMD_COMPLETE, the input argument is the selected drive;
326 otherwise, it is drive 0.
327 */
328
rx_svc(UNIT * uptr)329 t_stat rx_svc (UNIT *uptr)
330 {
331 int32 i, func;
332 uint32 da;
333 int8 *fbuf = uptr->filebuf;
334
335 func = RXCS_GETFNC (rx_csr); /* get function */
336 switch (rx_state) { /* case on state */
337
338 case IDLE: /* idle */
339 return SCPE_IERR; /* done */
340
341 case EMPTY: /* empty buffer */
342 if (rx_bptr >= RX_NUMBY) /* done all? */
343 rx_done (0, 0);
344 else {
345 rx_dbr = rx_buf[rx_bptr]; /* get next */
346 rx_bptr = rx_bptr + 1;
347 rx_csr = rx_csr | RXCS_TR; /* set xfer */
348 }
349 break;
350
351 case FILL: /* fill buffer */
352 rx_buf[rx_bptr] = rx_dbr; /* write next */
353 rx_bptr = rx_bptr + 1;
354 if (rx_bptr < RX_NUMBY) /* more? set xfer */
355 rx_csr = rx_csr | RXCS_TR;
356 else rx_done (0, 0); /* else done */
357 break;
358
359 case RWDS: /* wait for sector */
360 rx_sector = rx_dbr & RX_M_SECTOR; /* save sector */
361 rx_csr = rx_csr | RXCS_TR; /* set xfer */
362 rx_state = RWDT; /* advance state */
363 return SCPE_OK;
364
365 case RWDT: /* wait for track */
366 rx_track = rx_dbr & RX_M_TRACK; /* save track */
367 rx_state = RWXFR;
368 sim_activate (uptr, /* sched done */
369 rx_swait * abs (rx_track - uptr->TRACK));
370 return SCPE_OK;
371
372 case RWXFR:
373 if ((uptr->flags & UNIT_BUF) == 0) { /* not buffered? */
374 rx_done (0, 0110); /* done, error */
375 return IORETURN (rx_stopioe, SCPE_UNATT);
376 }
377 if (rx_track >= RX_NUMTR) { /* bad track? */
378 rx_done (0, 0040); /* done, error */
379 break;
380 }
381 uptr->TRACK = rx_track; /* now on track */
382 if ((rx_sector == 0) || (rx_sector > RX_NUMSC)) { /* bad sect? */
383 rx_done (0, 0070); /* done, error */
384 break;
385 }
386 da = CALC_DA (rx_track, rx_sector); /* get disk address */
387 if (func == RXCS_WRDEL) /* del data? */
388 rx_esr = rx_esr | RXES_DD;
389 if (func == RXCS_READ) { /* read? */
390 for (i = 0; i < RX_NUMBY; i++)
391 rx_buf[i] = fbuf[da + i];
392 }
393 else {
394 if (uptr->flags & UNIT_WPRT) { /* write and locked? */
395 rx_done (RXES_WLK, 0100); /* done, error */
396 break;
397 }
398 for (i = 0; i < RX_NUMBY; i++) /* write */
399 fbuf[da + i] = rx_buf[i];
400 da = da + RX_NUMBY;
401 if (da > uptr->hwmark)
402 uptr->hwmark = da;
403 }
404 rx_done (0, 0); /* done */
405 break;
406
407 case CMD_COMPLETE: /* command complete */
408 if (func == RXCS_ECODE) { /* read ecode? */
409 rx_dbr = rx_ecode; /* set dbr */
410 rx_done (0, -1); /* don't update */
411 }
412 else rx_done (0, 0);
413 break;
414
415 case INIT_COMPLETE: /* init complete */
416 rx_unit[0].TRACK = 1; /* drive 0 to trk 1 */
417 rx_unit[1].TRACK = 0; /* drive 1 to trk 0 */
418 if ((rx_unit[0].flags & UNIT_BUF) == 0) { /* not buffered? */
419 rx_done (RXES_ID, 0010); /* init done, error */
420 break;
421 }
422 da = CALC_DA (1, 1); /* track 1, sector 1 */
423 for (i = 0; i < RX_NUMBY; i++) /* read sector */
424 rx_buf[i] = fbuf[da + i];
425 rx_done (RXES_ID, 0); /* set done */
426 if ((rx_unit[1].flags & UNIT_ATT) == 0)
427 rx_ecode = 0020;
428 break;
429 } /* end case state */
430
431 return SCPE_OK;
432 }
433
434 /* Command complete. Set done and put final value in interface register,
435 request interrupt if needed, return to IDLE state.
436 */
437
rx_done(int32 esr_flags,int32 new_ecode)438 void rx_done (int32 esr_flags, int32 new_ecode)
439 {
440 int32 drv = (rx_csr & RXCS_DRV)? 1: 0;
441
442 rx_state = IDLE; /* now idle */
443 rx_csr = rx_csr | RXCS_DONE; /* set done */
444 if (rx_csr & RXCS_IE) SET_INT (RX); /* if ie, intr */
445 rx_esr = (rx_esr | esr_flags) & ~RXES_DRDY;
446 if (rx_unit[drv].flags & UNIT_ATT)
447 rx_esr = rx_esr | RXES_DRDY;
448 if (new_ecode > 0) /* test for error */
449 rx_csr = rx_csr | RXCS_ERR;
450 if (new_ecode < 0) /* don't update? */
451 return;
452 rx_ecode = new_ecode; /* update ecode */
453 rx_dbr = rx_esr; /* update RXDB */
454 return;
455 }
456
457 /* Device initialization. The RX is one of the few devices that schedules
458 an I/O transfer as part of its initialization.
459 */
460
rx_reset(DEVICE * dptr)461 t_stat rx_reset (DEVICE *dptr)
462 {
463 rx_csr = rx_dbr = 0; /* clear regs */
464 rx_esr = rx_ecode = 0; /* clear error */
465 rx_track = rx_sector = 0; /* clear addr */
466 rx_state = IDLE; /* ctrl idle */
467 CLR_INT (RX); /* clear int req */
468 sim_cancel (&rx_unit[1]); /* cancel drive 1 */
469 if (dptr->flags & DEV_DIS) /* disabled? */
470 sim_cancel (&rx_unit[0]);
471 else if (rx_unit[0].flags & UNIT_BUF) { /* attached? */
472 rx_state = INIT_COMPLETE; /* yes, sched init */
473 sim_activate (&rx_unit[0], rx_swait * abs (1 - rx_unit[0].TRACK));
474 }
475 else rx_done (0, 0010); /* no, error */
476 return auto_config (0, 0); /* run autoconfig */
477 }
478
479 /* Device bootstrap */
480
481 #define BOOT_START 02000 /* start */
482 #define BOOT_ENTRY (BOOT_START + 002) /* entry */
483 #define BOOT_UNIT (BOOT_START + 010) /* unit number */
484 #define BOOT_CSR (BOOT_START + 026) /* CSR */
485 #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16))
486
487 static const uint16 boot_rom[] = {
488 042130, /* "XD" */
489 0012706, BOOT_START, /* MOV #boot_start, SP */
490 0012700, 0000000, /* MOV #unit, R0 ; unit number */
491 0010003, /* MOV R0, R3 */
492 0006303, /* ASL R3 */
493 0006303, /* ASL R3 */
494 0006303, /* ASL R3 */
495 0006303, /* ASL R3 */
496 0012701, 0177170, /* MOV #RXCS, R1 ; csr */
497 0032711, 0000040, /* BITB #40, (R1) ; ready? */
498 0001775, /* BEQ .-4 */
499 0052703, 0000007, /* BIS #READ+GO, R3 */
500 0010311, /* MOV R3, (R1) ; read & go */
501 0105711, /* TSTB (R1) ; xfr ready? */
502 0100376, /* BPL .-2 */
503 0012761, 0000001, 0000002, /* MOV #1, 2(R1) ; sector */
504 0105711, /* TSTB (R1) ; xfr ready? */
505 0100376, /* BPL .-2 */
506 0012761, 0000001, 0000002, /* MOV #1, 2(R1) ; track */
507 0005003, /* CLR R3 */
508 0032711, 0000040, /* BITB #40, (R1) ; ready? */
509 0001775, /* BEQ .-4 */
510 0012711, 0000003, /* MOV #EMPTY+GO, (R1) ; empty & go */
511 0105711, /* TSTB (R1) ; xfr, done? */
512 0001776, /* BEQ .-2 */
513 0100003, /* BPL .+010 */
514 0116123, 0000002, /* MOVB 2(R1), (R3)+ ; move byte */
515 0000772, /* BR .-012 */
516 0005002, /* CLR R2 */
517 0005003, /* CLR R3 */
518 0012704, BOOT_START+020, /* MOV #START+20, R4 */
519 0005005, /* CLR R5 */
520 0005007 /* CLR R7 */
521 };
522
rx_boot(int32 unitno,DEVICE * dptr)523 t_stat rx_boot (int32 unitno, DEVICE *dptr)
524 {
525 int32 i;
526 extern int32 saved_PC;
527 extern uint16 *M;
528
529 for (i = 0; i < BOOT_LEN; i++)
530 M[(BOOT_START >> 1) + i] = boot_rom[i];
531 M[BOOT_UNIT >> 1] = unitno & RX_M_NUMDR;
532 M[BOOT_CSR >> 1] = rx_dib.ba & DMASK;
533 saved_PC = BOOT_ENTRY;
534 return SCPE_OK;
535 }
536