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