First Commit of my working state
[simh.git] / PDP18B / pdp18b_rp.c
CommitLineData
196ba1fc
PH
1/* pdp18b_rp.c: RP15/RP02 disk pack simulator\r
2\r
3 Copyright (c) 1993-2005, 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 rp RP15/RP02 disk pack\r
27\r
28 14-Jan-04 RMS Revised IO device call interface\r
29 06-Feb-03 RMS Revised IOT decoding, fixed bug in initiation\r
30 05-Oct-02 RMS Added DIB, device number support\r
31 06-Jan-02 RMS Revised enable/disable support\r
32 29-Nov-01 RMS Added read only unit support\r
33 25-Nov-01 RMS Revised interrupt structure\r
34 Changed FLG to array\r
35 26-Apr-01 RMS Added device enable/disable support\r
36 14-Apr-99 RMS Changed t_addr to unsigned\r
37 29-Jun-96 RMS Added unit enable/disable support\r
38*/\r
39\r
40#include "pdp18b_defs.h"\r
41\r
42/* Constants */\r
43\r
44#define RP_NUMWD 256 /* words/sector */\r
45#define RP_NUMSC 10 /* sectors/surface */\r
46#define RP_NUMSF 20 /* surfaces/cylinder */\r
47#define RP_NUMCY 203 /* cylinders/drive */\r
48#define RP_NUMDR 8 /* drives/controller */\r
49#define RP_SIZE (RP_NUMCY * RP_NUMSF * RP_NUMSC * RP_NUMWD)\r
50 /* words/drive */\r
51\r
52/* Unit specific flags */\r
53\r
54#define UNIT_V_WLK (UNIT_V_UF + 0) /* hwre write lock */\r
55#define UNIT_WLK (1u << UNIT_V_WLK)\r
56#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */\r
57\r
58/* Parameters in the unit descriptor */\r
59\r
60#define CYL u3 /* current cylinder */\r
61#define FUNC u4 /* function */\r
62\r
63/* Status register A */\r
64\r
65#define STA_V_UNIT 15 /* unit select */\r
66#define STA_M_UNIT 07\r
67#define STA_V_FUNC 12 /* function */\r
68#define STA_M_FUNC 07\r
69#define FN_IDLE 0\r
70#define FN_READ 1\r
71#define FN_WRITE 2\r
72#define FN_RECAL 3\r
73#define FN_SEEK 4\r
74#define FN_RDALL 5\r
75#define FN_WRALL 6\r
76#define FN_WRCHK 7\r
77#define FN_2ND 010 /* second state flag */\r
78#define STA_IED 0004000 /* int enable done */\r
79#define STA_IEA 0002000 /* int enable attn */\r
80#define STA_GO 0001000 /* go */\r
81#define STA_WPE 0000400 /* write lock error */\r
82#define STA_NXC 0000200 /* nx cyl error */\r
83#define STA_NXF 0000100 /* nx surface error */\r
84#define STA_NXS 0000040 /* nx sector error */\r
85#define STA_HNF 0000020 /* hdr not found */\r
86#define STA_SUWP 0000010 /* sel unit wrt lock */\r
87#define STA_SUSI 0000004 /* sel unit seek inc */\r
88#define STA_DON 0000002 /* done */\r
89#define STA_ERR 0000001 /* error */\r
90\r
91#define STA_RW 0777000 /* read/write */\r
92#define STA_EFLGS (STA_WPE | STA_NXC | STA_NXF | STA_NXS | \\r
93 STA_HNF | STA_SUSI) /* error flags */\r
94#define STA_DYN (STA_SUWP | STA_SUSI) /* per unit status */\r
95#define GET_UNIT(x) (((x) >> STA_V_UNIT) & STA_M_UNIT)\r
96#define GET_FUNC(x) (((x) >> STA_V_FUNC) & STA_M_FUNC)\r
97\r
98/* Status register B */\r
99\r
100#define STB_V_ATT0 17 /* unit 0 attention */\r
101#define STB_ATTN 0776000 /* attention flags */\r
102#define STB_SUFU 0001000 /* sel unit unsafe */\r
103#define STB_PGE 0000400 /* programming error */\r
104#define STB_EOP 0000200 /* end of pack */\r
105#define STB_TME 0000100 /* timing error */\r
106#define STB_FME 0000040 /* format error */\r
107#define STB_WCE 0000020 /* write check error */\r
108#define STB_WPE 0000010 /* word parity error */\r
109#define STB_LON 0000004 /* long parity error */\r
110#define STB_SUSU 0000002 /* sel unit seeking */\r
111#define STB_SUNR 0000001 /* sel unit not rdy */\r
112\r
113#define STB_EFLGS (STB_SUFU | STB_PGE | STB_EOP | STB_TME | STB_FME | \\r
114 STB_WCE | STB_WPE | STB_LON ) /* error flags */\r
115#define STB_DYN (STB_SUFU | STB_SUSU | STB_SUNR) /* per unit */\r
116\r
117/* Disk address */\r
118\r
119#define DA_V_SECT 0 /* sector */\r
120#define DA_M_SECT 017\r
121#define DA_V_SURF 5\r
122#define DA_M_SURF 037\r
123#define DA_V_CYL 10 /* cylinder */\r
124#define DA_M_CYL 0377\r
125#define GET_SECT(x) (((x) >> DA_V_SECT) & DA_M_SECT)\r
126#define GET_SURF(x) (((x) >> DA_V_SURF) & DA_M_SURF)\r
127#define GET_CYL(x) (((x) >> DA_V_CYL) & DA_M_CYL)\r
128#define GET_DA(x) ((((GET_CYL (x) * RP_NUMSF) + GET_SURF (x)) * \\r
129 RP_NUMSC) + GET_SECT (x))\r
130\r
131#define RP_MIN 2\r
132#define MAX(x,y) (((x) > (y))? (x): (y))\r
133\r
134extern int32 M[];\r
135extern int32 int_hwre[API_HLVL+1], nexm;\r
136extern UNIT cpu_unit;\r
137\r
138int32 rp_sta = 0; /* status A */\r
139int32 rp_stb = 0; /* status B */\r
140int32 rp_ma = 0; /* memory address */\r
141int32 rp_da = 0; /* disk address */\r
142int32 rp_wc = 0; /* word count */\r
143int32 rp_busy = 0; /* busy */\r
144int32 rp_stopioe = 1; /* stop on error */\r
145int32 rp_swait = 10; /* seek time */\r
146int32 rp_rwait = 10; /* rotate time */\r
147\r
148DEVICE rp_dev;\r
149int32 rp63 (int32 dev, int32 pulse, int32 dat);\r
150int32 rp64 (int32 dev, int32 pulse, int32 dat);\r
151int32 rp_iors (void);\r
152t_stat rp_svc (UNIT *uptr);\r
153void rp_updsta (int32 newa, int32 newb);\r
154t_stat rp_reset (DEVICE *dptr);\r
155t_stat rp_attach (UNIT *uptr, char *cptr);\r
156t_stat rp_detach (UNIT *uptr);\r
157\r
158/* RP15 data structures\r
159\r
160 rp_dev RP device descriptor\r
161 rp_unit RP unit list\r
162 rp_reg RP register list\r
163 rp_mod RP modifier list\r
164*/\r
165\r
166DIB rp_dib = { DEV_RP, 2, &rp_iors, { &rp63, &rp64 } };\r
167\r
168UNIT rp_unit[] = {\r
169 { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) },\r
170 { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) },\r
171 { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) },\r
172 { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) },\r
173 { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) },\r
174 { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) },\r
175 { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) },\r
176 { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) }\r
177 };\r
178\r
179REG rp_reg[] = {\r
180 { ORDATA (STA, rp_sta, 18) },\r
181 { ORDATA (STB, rp_stb, 18) },\r
182 { ORDATA (DA, rp_da, 18) },\r
183 { ORDATA (MA, rp_ma, 18) },\r
184 { ORDATA (WC, rp_wc, 18) },\r
185 { FLDATA (INT, int_hwre[API_RP], INT_V_RP) },\r
186 { FLDATA (BUSY, rp_busy, 0) },\r
187 { FLDATA (STOP_IOE, rp_stopioe, 0) },\r
188 { DRDATA (STIME, rp_swait, 24), PV_LEFT },\r
189 { DRDATA (RTIME, rp_rwait, 24), PV_LEFT },\r
190 { ORDATA (DEVNO, rp_dib.dev, 6), REG_HRO },\r
191 { NULL }\r
192 };\r
193\r
194MTAB rp_mod[] = {\r
195 { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },\r
196 { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },\r
197 { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_devno, &show_devno },\r
198 { 0 }\r
199 };\r
200\r
201DEVICE rp_dev = {\r
202 "RP", rp_unit, rp_reg, rp_mod,\r
203 RP_NUMDR, 8, 24, 1, 8, 18,\r
204 NULL, NULL, &rp_reset,\r
205 NULL, &rp_attach, &rp_detach,\r
206 &rp_dib, DEV_DISABLE\r
207 };\r
208\r
209/* IOT routines */\r
210\r
211int32 rp63 (int32 dev, int32 pulse, int32 dat)\r
212{\r
213int32 sb = pulse & 060; /* subopcode */\r
214\r
215rp_updsta (0, 0);\r
216if (pulse & 01) {\r
217 if ((sb == 000) && /* DPSF */\r
218 ((rp_sta & (STA_DON | STA_ERR)) || (rp_stb & STB_ATTN)))\r
219 dat = IOT_SKP | dat;\r
220 else if ((sb == 020) && (rp_stb & STB_ATTN)) /* DPSA */\r
221 dat = IOT_SKP | dat;\r
222 else if ((sb == 040) && (rp_sta & STA_DON)) /* DPSJ */\r
223 dat = IOT_SKP | dat;\r
224 else if ((sb == 060) && (rp_sta & STA_ERR)) /* DPSE */\r
225 dat = IOT_SKP | dat;\r
226 }\r
227if (pulse & 02) {\r
228 if (sb == 000) dat = dat | rp_sta; /* DPOSA */\r
229 else if (sb == 020) dat = dat | rp_stb; /* DPOSB */\r
230 }\r
231if (pulse & 04) {\r
232 if (rp_busy) { /* busy? */\r
233 rp_updsta (0, STB_PGE);\r
234 return dat;\r
235 }\r
236 else if (sb == 000) { /* DPLA */\r
237 rp_da = dat & DMASK;\r
238 if (GET_SECT (rp_da) >= RP_NUMSC) rp_updsta (STA_NXS, 0);\r
239 if (GET_SURF (rp_da) >= RP_NUMSF) rp_updsta (STA_NXF, 0);\r
240 if (GET_CYL (rp_da) >= RP_NUMCY) rp_updsta (STA_NXC, 0);\r
241 }\r
242 else if (sb == 020) { /* DPCS */\r
243 rp_sta = rp_sta & ~(STA_HNF | STA_DON);\r
244 rp_stb = rp_stb & ~(STB_FME | STB_WPE | STB_LON | STB_WCE |\r
245 STB_TME | STB_PGE | STB_EOP);\r
246 rp_updsta (0, 0);\r
247 }\r
248 else if (sb == 040) rp_ma = dat & DMASK; /* DPCA */\r
249 else if (sb == 060) rp_wc = dat & DMASK; /* DPWC */\r
250 }\r
251return dat;\r
252}\r
253\r
254/* IOT 64 */\r
255\r
256int32 rp64 (int32 dev, int32 pulse, int32 dat)\r
257{\r
258int32 u, f, c, sb;\r
259UNIT *uptr;\r
260\r
261sb = pulse & 060;\r
262if (pulse & 01) {\r
263 if (sb == 020) dat = IOT_SKP | dat; /* DPSN */\r
264 }\r
265if (pulse & 02) {\r
266 if (sb == 000) /* DPOU */\r
267 dat = dat | rp_unit[GET_UNIT (rp_sta)].CYL;\r
268 else if (sb == 020) dat = dat | rp_da; /* DPOA */\r
269 else if (sb == 040) dat = dat | rp_ma; /* DPOC */\r
270 else if (sb == 060) dat = dat | rp_wc; /* DPOW */\r
271 }\r
272if (pulse & 04) {\r
273 if (rp_busy) { /* busy? */\r
274 rp_updsta (0, STB_PGE);\r
275 return dat;\r
276 }\r
277 if (sb == 000) /* DPCF */\r
278 rp_sta = rp_sta & ~STA_RW;\r
279 else if (sb == 020) /* DPLZ */\r
280 rp_sta = rp_sta & (dat | ~STA_RW);\r
281 else if (sb == 040) /* DPLO */\r
282 rp_sta = rp_sta | (dat & STA_RW);\r
283 else if (sb == 060) /* DPLF */\r
284 rp_sta = (rp_sta & ~STA_RW) | (dat & STA_RW);\r
285 rp_sta = rp_sta & ~STA_DON; /* clear done */\r
286 u = GET_UNIT (rp_sta); /* get unit num */\r
287 uptr = rp_dev.units + u; /* select unit */\r
288 if ((rp_sta & STA_GO) && !sim_is_active (uptr)) {\r
289 f = uptr->FUNC = GET_FUNC (rp_sta); /* get function */\r
290 rp_busy = 1; /* set ctrl busy */\r
291 rp_sta = rp_sta & ~(STA_HNF | STA_DON); /* clear flags */\r
292 rp_stb = rp_stb & ~(STB_FME | STB_WPE | STB_LON | STB_WCE |\r
293 STB_TME | STB_PGE | STB_EOP | (1 << (STB_V_ATT0 - u)));\r
294 if (((uptr->flags & UNIT_ATT) == 0) || (f == FN_IDLE) ||\r
295 (f == FN_SEEK) || (f == FN_RECAL))\r
296 sim_activate (uptr, RP_MIN); /* short delay */\r
297 else {\r
298 c = GET_CYL (rp_da);\r
299 c = abs (c - uptr->CYL) * rp_swait; /* seek time */\r
300 sim_activate (uptr, MAX (RP_MIN, c + rp_rwait));\r
301 }\r
302 }\r
303 }\r
304rp_updsta (0, 0);\r
305return dat;\r
306}\r
307\r
308/* Unit service\r
309\r
310 If function = idle, clear busy\r
311 If seek or recal initial state, clear attention line, compute seek time,\r
312 put on cylinder, set second state\r
313 If unit not attached, give error\r
314 If seek or recal second state, set attention line, compute errors\r
315 Else complete data transfer command\r
316\r
317 The unit control block contains the function and cylinder for\r
318 the current command.\r
319*/\r
320\r
321static int32 fill[RP_NUMWD] = { 0 };\r
322t_stat rp_svc (UNIT *uptr)\r
323{\r
324int32 f, u, comp, cyl, sect, surf;\r
325int32 err, pa, da, wc, awc, i;\r
326\r
327u = (int32) (uptr - rp_dev.units); /* get drv number */\r
328f = uptr->FUNC; /* get function */\r
329if (f == FN_IDLE) { /* idle? */\r
330 rp_busy = 0; /* clear busy */\r
331 return SCPE_OK;\r
332 }\r
333\r
334if ((f == FN_SEEK) || (f == FN_RECAL)) { /* seek or recal? */\r
335 rp_busy = 0; /* not busy */\r
336 cyl = (f == FN_SEEK)? GET_CYL (rp_da): 0; /* get cylinder */\r
337 sim_activate (uptr, MAX (RP_MIN, abs (cyl - uptr->CYL) * rp_swait));\r
338 uptr->CYL = cyl; /* on cylinder */\r
339 uptr->FUNC = FN_SEEK | FN_2ND; /* set second state */\r
340 rp_updsta (0, 0); /* update status */\r
341 return SCPE_OK;\r
342 }\r
343\r
344if (f == (FN_SEEK | FN_2ND)) { /* seek done? */\r
345 rp_updsta (0, rp_stb | (1 << (STB_V_ATT0 - u))); /* set attention */\r
346 return SCPE_OK;\r
347 }\r
348\r
349if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */\r
350 rp_updsta (STA_DON, STB_SUFU); /* done, unsafe */\r
351 return IORETURN (rp_stopioe, SCPE_UNATT);\r
352 }\r
353\r
354if ((f == FN_WRITE) && (uptr->flags & UNIT_WPRT)) { /* write locked? */\r
355 rp_updsta (STA_DON | STA_WPE, 0); /* error */\r
356 return SCPE_OK;\r
357 }\r
358\r
359if (GET_SECT (rp_da) >= RP_NUMSC) rp_updsta (STA_NXS, 0);\r
360if (GET_SURF (rp_da) >= RP_NUMSF) rp_updsta (STA_NXF, 0);\r
361if (GET_CYL (rp_da) >= RP_NUMCY) rp_updsta (STA_NXC, 0);\r
362if (rp_sta & (STA_NXS | STA_NXF | STA_NXC)) { /* or bad disk addr? */\r
363 rp_updsta (STA_DON, STB_SUFU); /* done, unsafe */\r
364 return SCPE_OK;\r
365 }\r
366\r
367pa = rp_ma & AMASK; /* get mem addr */\r
368da = GET_DA (rp_da) * RP_NUMWD; /* get disk addr */\r
369wc = 01000000 - rp_wc; /* get true wc */\r
370if (((uint32) (pa + wc)) > MEMSIZE) { /* memory overrun? */\r
371 nexm = 1; /* set nexm flag */\r
372 wc = MEMSIZE - pa; /* limit xfer */\r
373 }\r
374if ((da + wc) > RP_SIZE) { /* disk overrun? */\r
375 rp_updsta (0, STB_EOP); /* error */\r
376 wc = RP_SIZE - da; /* limit xfer */\r
377 }\r
378\r
379err = fseek (uptr->fileref, da * sizeof (int), SEEK_SET);\r
380\r
381if ((f == FN_READ) && (err == 0)) { /* read? */\r
382 awc = fxread (&M[pa], sizeof (int32), wc, uptr->fileref);\r
383 for ( ; awc < wc; awc++) M[pa + awc] = 0;\r
384 err = ferror (uptr->fileref);\r
385 }\r
386\r
387if ((f == FN_WRITE) && (err == 0)) { /* write? */\r
388 fxwrite (&M[pa], sizeof (int32), wc, uptr->fileref);\r
389 err = ferror (uptr->fileref);\r
390 if ((err == 0) && (i = (wc & (RP_NUMWD - 1)))) {\r
391 fxwrite (fill, sizeof (int), i, uptr->fileref);\r
392 err = ferror (uptr->fileref);\r
393 }\r
394 }\r
395\r
396if ((f == FN_WRCHK) && (err == 0)) { /* write check? */\r
397 for (i = 0; (err == 0) && (i < wc); i++) {\r
398 awc = fxread (&comp, sizeof (int32), 1, uptr->fileref);\r
399 if (awc == 0) comp = 0;\r
400 if (comp != M[pa + i]) rp_updsta (0, STB_WCE);\r
401 }\r
402 err = ferror (uptr->fileref);\r
403 }\r
404\r
405rp_wc = (rp_wc + wc) & DMASK; /* final word count */\r
406rp_ma = (rp_ma + wc) & DMASK; /* final mem addr */\r
407da = (da + wc + (RP_NUMWD - 1)) / RP_NUMWD; /* final sector num */\r
408cyl = da / (RP_NUMSC * RP_NUMSF); /* get cyl */\r
409if (cyl >= RP_NUMCY) cyl = RP_NUMCY - 1;\r
410surf = (da % (RP_NUMSC * RP_NUMSF)) / RP_NUMSC; /* get surface */\r
411sect = (da % (RP_NUMSC * RP_NUMSF)) % RP_NUMSC; /* get sector */\r
412rp_da = (cyl << DA_V_CYL) | (surf << DA_V_SURF) | (sect << DA_V_SECT);\r
413rp_busy = 0; /* clear busy */\r
414rp_updsta (STA_DON, 0); /* set done */\r
415\r
416if (err != 0) { /* error? */\r
417 perror ("RP I/O error");\r
418 clearerr (uptr->fileref);\r
419 return IORETURN (rp_stopioe, SCPE_IOERR);\r
420 }\r
421return SCPE_OK;\r
422}\r
423\r
424/* Update status */\r
425\r
426void rp_updsta (int32 newa, int32 newb)\r
427{\r
428int32 f;\r
429UNIT *uptr;\r
430\r
431uptr = rp_dev.units + GET_UNIT (rp_sta);\r
432rp_sta = (rp_sta & ~(STA_DYN | STA_ERR)) | newa;\r
433rp_stb = (rp_stb & ~STB_DYN) | newb;\r
434if (uptr->flags & UNIT_WPRT) rp_sta = rp_sta | STA_SUWP;\r
435if ((uptr->flags & UNIT_ATT) == 0) rp_stb = rp_stb | STB_SUFU | STB_SUNR;\r
436else if (sim_is_active (uptr)) {\r
437 f = (uptr->FUNC) & STA_M_FUNC;\r
438 if ((f == FN_SEEK) || (f == FN_RECAL))\r
439 rp_stb = rp_stb | STB_SUSU | STB_SUNR;\r
440 }\r
441else if (uptr->CYL >= RP_NUMCY) rp_sta = rp_sta | STA_SUSI;\r
442if ((rp_sta & STA_EFLGS) || (rp_stb & STB_EFLGS)) rp_sta = rp_sta | STA_ERR;\r
443if (((rp_sta & (STA_ERR | STA_DON)) && (rp_sta & STA_IED)) ||\r
444 ((rp_stb & STB_ATTN) && (rp_sta & STA_IEA))) SET_INT (RP);\r
445else CLR_INT (RP);\r
446return;\r
447}\r
448\r
449/* Reset routine */\r
450\r
451t_stat rp_reset (DEVICE *dptr)\r
452{\r
453int32 i;\r
454UNIT *uptr;\r
455\r
456rp_sta = rp_stb = rp_da = rp_wc = rp_ma = rp_busy = 0;\r
457CLR_INT (RP);\r
458for (i = 0; i < RP_NUMDR; i++) {\r
459 uptr = rp_dev.units + i;\r
460 sim_cancel (uptr);\r
461 uptr->CYL = uptr->FUNC = 0;\r
462 }\r
463return SCPE_OK;\r
464}\r
465\r
466/* IORS routine */\r
467\r
468int32 rp_iors (void)\r
469{\r
470return ((rp_sta & (STA_ERR | STA_DON)) || (rp_stb & STB_ATTN))? IOS_RP: 0;\r
471}\r
472\r
473/* Attach unit */\r
474\r
475t_stat rp_attach (UNIT *uptr, char *cptr)\r
476{\r
477t_stat reason;\r
478\r
479reason = attach_unit (uptr, cptr);\r
480rp_updsta (0, 0);\r
481return reason;\r
482}\r
483\r
484/* Detach unit */\r
485\r
486t_stat rp_detach (UNIT *uptr)\r
487{\r
488t_stat reason;\r
489\r
490reason = detach_unit (uptr);\r
491rp_updsta (0, 0);\r
492return reason;\r
493}\r