Commit | Line | Data |
---|---|---|
196ba1fc PH |
1 | /* pdp11_rk.c: RK11/RKV11 cartridge disk 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 | rk RK11/RKV11/RK05 cartridge disk\r | |
27 | \r | |
28 | 16-Aug-05 RMS Fixed C++ declaration and cast problems\r | |
29 | 07-Jul-05 RMS Removed extraneous externs\r | |
30 | 30-Sep-04 RMS Revised Unibus interface\r | |
31 | 24-Jan-04 RMS Added increment inhibit, overrun detection, formatting\r | |
32 | 29-Dec-03 RMS Added RKV11 support\r | |
33 | 29-Sep-02 RMS Added variable address support to bootstrap\r | |
34 | Added vector change/display support\r | |
35 | Revised mapping mnemonics\r | |
36 | New data structures\r | |
37 | 26-Jan-02 RMS Revised bootstrap to conform to M9312\r | |
38 | 06-Jan-02 RMS Revised 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 | 09-Nov-01 RMS Added bus map support\r | |
42 | 07-Sep-01 RMS Revised device disable and interrupt mechanisms\r | |
43 | 26-Apr-01 RMS Added device enable/disable support\r | |
44 | 25-Mar-01 RMS Fixed block fill calculation\r | |
45 | 15-Feb-01 RMS Corrected bootstrap string\r | |
46 | 29-Jun-96 RMS Added unit disable support\r | |
47 | \r | |
48 | The RK11 is an eight drive cartridge disk subsystem. An RK05 drive\r | |
49 | consists of 203 cylinders, each with 2 surfaces containing 12 sectors\r | |
50 | of 512 bytes.\r | |
51 | \r | |
52 | The most complicated part of the RK11 controller is the concept of\r | |
53 | interrupt "polling". While only one read or write can occur at a\r | |
54 | time, the controller supports multiple seeks. When a seek completes,\r | |
55 | if done is set the drive attempts to interrupt. If an interrupt is\r | |
56 | already pending, the interrupt is "queued" until it can be processed.\r | |
57 | When an interrupt occurs, RKDS<15:13> is loaded with the number of the\r | |
58 | interrupting drive.\r | |
59 | \r | |
60 | To implement this structure, and to assure that read/write interrupts\r | |
61 | take priority over seek interrupts, the controller contains an\r | |
62 | interrupt queue, rkintq, with a bit for a controller interrupt and\r | |
63 | then one for each drive. In addition, the drive number of the last\r | |
64 | non-seeking drive is recorded in last_drv.\r | |
65 | */\r | |
66 | \r | |
67 | #include "pdp11_defs.h"\r | |
68 | \r | |
69 | /* Constants */\r | |
70 | \r | |
71 | #define RK_NUMWD 256 /* words/sector */\r | |
72 | #define RK_NUMSC 12 /* sectors/surface */\r | |
73 | #define RK_NUMSF 2 /* surfaces/cylinder */\r | |
74 | #define RK_NUMCY 203 /* cylinders/drive */\r | |
75 | #define RK_NUMTR (RK_NUMCY * RK_NUMSF) /* tracks/drive */\r | |
76 | #define RK_NUMDR 8 /* drives/controller */\r | |
77 | #define RK_M_NUMDR 07\r | |
78 | #define RK_SIZE (RK_NUMCY * RK_NUMSF * RK_NUMSC * RK_NUMWD)\r | |
79 | /* words/drive */\r | |
80 | #define RK_CTLI 1 /* controller int */\r | |
81 | #define RK_SCPI(x) (2u << (x)) /* drive int */\r | |
82 | #define RK_MAXFR (1 << 16) /* max transfer */\r | |
83 | \r | |
84 | /* Flags in the unit flags word */\r | |
85 | \r | |
86 | #define UNIT_V_HWLK (UNIT_V_UF + 0) /* hwre write lock */\r | |
87 | #define UNIT_V_SWLK (UNIT_V_UF + 1) /* swre write lock */\r | |
88 | #define UNIT_HWLK (1u << UNIT_V_HWLK)\r | |
89 | #define UNIT_SWLK (1u << UNIT_V_SWLK)\r | |
90 | #define UNIT_WPRT (UNIT_HWLK|UNIT_SWLK|UNIT_RO) /* write prot */\r | |
91 | \r | |
92 | /* Parameters in the unit descriptor */\r | |
93 | \r | |
94 | #define CYL u3 /* current cylinder */\r | |
95 | #define FUNC u4 /* function */\r | |
96 | \r | |
97 | /* RKDS */\r | |
98 | \r | |
99 | #define RKDS_SC 0000017 /* sector counter */\r | |
100 | #define RKDS_ON_SC 0000020 /* on sector */\r | |
101 | #define RKDS_WLK 0000040 /* write locked */\r | |
102 | #define RKDS_RWS 0000100 /* rd/wr/seek ready */\r | |
103 | #define RKDS_RDY 0000200 /* drive ready */\r | |
104 | #define RKDS_SC_OK 0000400 /* SC valid */\r | |
105 | #define RKDS_INC 0001000 /* seek incomplete */\r | |
106 | #define RKDS_UNSAFE 0002000 /* unsafe */\r | |
107 | #define RKDS_RK05 0004000 /* RK05 */\r | |
108 | #define RKDS_PWR 0010000 /* power low */\r | |
109 | #define RKDS_ID 0160000 /* drive ID */\r | |
110 | #define RKDS_V_ID 13\r | |
111 | \r | |
112 | /* RKER */\r | |
113 | \r | |
114 | #define RKER_WCE 0000001 /* write check */\r | |
115 | #define RKER_CSE 0000002 /* checksum */\r | |
116 | #define RKER_NXS 0000040 /* nx sector */\r | |
117 | #define RKER_NXC 0000100 /* nx cylinder */\r | |
118 | #define RKER_NXD 0000200 /* nx drive */\r | |
119 | #define RKER_TE 0000400 /* timing error */\r | |
120 | #define RKER_DLT 0001000 /* data late */\r | |
121 | #define RKER_NXM 0002000 /* nx memory */\r | |
122 | #define RKER_PGE 0004000 /* programming error */\r | |
123 | #define RKER_SKE 0010000 /* seek error */\r | |
124 | #define RKER_WLK 0020000 /* write lock */\r | |
125 | #define RKER_OVR 0040000 /* overrun */\r | |
126 | #define RKER_DRE 0100000 /* drive error */\r | |
127 | #define RKER_IMP 0177743 /* implemented */\r | |
128 | #define RKER_SOFT (RKER_WCE+RKER_CSE) /* soft errors */\r | |
129 | #define RKER_HARD 0177740 /* hard errors */\r | |
130 | \r | |
131 | /* RKCS */\r | |
132 | \r | |
133 | #define RKCS_M_FUNC 0000007 /* function */\r | |
134 | #define RKCS_CTLRESET 0\r | |
135 | #define RKCS_WRITE 1\r | |
136 | #define RKCS_READ 2\r | |
137 | #define RKCS_WCHK 3\r | |
138 | #define RKCS_SEEK 4\r | |
139 | #define RKCS_RCHK 5\r | |
140 | #define RKCS_DRVRESET 6\r | |
141 | #define RKCS_WLK 7\r | |
142 | #define RKCS_V_FUNC 1\r | |
143 | #define RKCS_MEX 0000060 /* memory extension */\r | |
144 | #define RKCS_V_MEX 4\r | |
145 | #define RKCS_SSE 0000400 /* stop on soft err */\r | |
146 | #define RKCS_FMT 0002000 /* format */\r | |
147 | #define RKCS_INH 0004000 /* inhibit increment */\r | |
148 | #define RKCS_SCP 0020000 /* search complete */\r | |
149 | #define RKCS_HERR 0040000 /* hard error */\r | |
150 | #define RKCS_ERR 0100000 /* error */\r | |
151 | #define RKCS_REAL 0026776 /* kept here */\r | |
152 | #define RKCS_RW 0006576 /* read/write */\r | |
153 | #define GET_FUNC(x) (((x) >> RKCS_V_FUNC) & RKCS_M_FUNC)\r | |
154 | \r | |
155 | /* RKDA */\r | |
156 | \r | |
157 | #define RKDA_V_SECT 0 /* sector */\r | |
158 | #define RKDA_M_SECT 017\r | |
159 | #define RKDA_V_TRACK 4 /* track */\r | |
160 | #define RKDA_M_TRACK 0777\r | |
161 | #define RKDA_V_CYL 5 /* cylinder */\r | |
162 | #define RKDA_M_CYL 0377\r | |
163 | #define RKDA_V_DRIVE 13 /* drive */\r | |
164 | #define RKDA_M_DRIVE 07\r | |
165 | #define RKDA_DRIVE (RKDA_M_DRIVE << RKDA_V_DRIVE)\r | |
166 | #define GET_SECT(x) (((x) >> RKDA_V_SECT) & RKDA_M_SECT)\r | |
167 | #define GET_CYL(x) (((x) >> RKDA_V_CYL) & RKDA_M_CYL)\r | |
168 | #define GET_TRACK(x) (((x) >> RKDA_V_TRACK) & RKDA_M_TRACK)\r | |
169 | #define GET_DRIVE(x) (((x) >> RKDA_V_DRIVE) & RKDA_M_DRIVE)\r | |
170 | #define GET_DA(x) ((GET_TRACK (x) * RK_NUMSC) + GET_SECT (x))\r | |
171 | \r | |
172 | /* RKBA */\r | |
173 | \r | |
174 | #define RKBA_IMP 0177776 /* implemented */\r | |
175 | \r | |
176 | #define RK_MIN 10\r | |
177 | #define MAX(x,y) (((x) > (y))? (x): (y))\r | |
178 | \r | |
179 | extern uint16 *M; /* memory */\r | |
180 | extern int32 int_req[IPL_HLVL];\r | |
181 | \r | |
182 | uint16 *rkxb = NULL; /* xfer buffer */\r | |
183 | int32 rkcs = 0; /* control/status */\r | |
184 | int32 rkds = 0; /* drive status */\r | |
185 | int32 rkba = 0; /* memory address */\r | |
186 | int32 rkda = 0; /* disk address */\r | |
187 | int32 rker = 0; /* error status */\r | |
188 | int32 rkwc = 0; /* word count */\r | |
189 | int32 rkintq = 0; /* interrupt queue */\r | |
190 | int32 last_drv = 0; /* last r/w drive */\r | |
191 | int32 rk_stopioe = 1; /* stop on error */\r | |
192 | int32 rk_swait = 10; /* seek time */\r | |
193 | int32 rk_rwait = 10; /* rotate time */\r | |
194 | \r | |
195 | DEVICE rk_dev;\r | |
196 | t_stat rk_rd (int32 *data, int32 PA, int32 access);\r | |
197 | t_stat rk_wr (int32 data, int32 PA, int32 access);\r | |
198 | int32 rk_inta (void);\r | |
199 | t_stat rk_svc (UNIT *uptr);\r | |
200 | t_stat rk_reset (DEVICE *dptr);\r | |
201 | void rk_go (void);\r | |
202 | void rk_set_done (int32 error);\r | |
203 | void rk_clr_done (void);\r | |
204 | t_stat rk_boot (int32 unitno, DEVICE *dptr);\r | |
205 | \r | |
206 | /* RK11 data structures\r | |
207 | \r | |
208 | rk_dev RK device descriptor\r | |
209 | rk_unit RK unit list\r | |
210 | rk_reg RK register list\r | |
211 | rk_mod RK modifier list\r | |
212 | */\r | |
213 | \r | |
214 | DIB rk_dib = {\r | |
215 | IOBA_RK, IOLN_RK, &rk_rd, &rk_wr,\r | |
216 | 1, IVCL (RK), VEC_RK, { &rk_inta }\r | |
217 | };\r | |
218 | \r | |
219 | UNIT rk_unit[] = {\r | |
220 | { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r | |
221 | UNIT_ROABLE, RK_SIZE) },\r | |
222 | { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r | |
223 | UNIT_ROABLE, RK_SIZE) },\r | |
224 | { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r | |
225 | UNIT_ROABLE, RK_SIZE) },\r | |
226 | { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r | |
227 | UNIT_ROABLE, RK_SIZE) },\r | |
228 | { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r | |
229 | UNIT_ROABLE, RK_SIZE) },\r | |
230 | { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r | |
231 | UNIT_ROABLE, RK_SIZE) },\r | |
232 | { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r | |
233 | UNIT_ROABLE, RK_SIZE) },\r | |
234 | { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r | |
235 | UNIT_ROABLE, RK_SIZE) }\r | |
236 | };\r | |
237 | \r | |
238 | REG rk_reg[] = {\r | |
239 | { ORDATA (RKCS, rkcs, 16) },\r | |
240 | { ORDATA (RKDA, rkda, 16) },\r | |
241 | { ORDATA (RKBA, rkba, 16) },\r | |
242 | { ORDATA (RKWC, rkwc, 16) },\r | |
243 | { ORDATA (RKDS, rkds, 16) },\r | |
244 | { ORDATA (RKER, rker, 16) },\r | |
245 | { ORDATA (INTQ, rkintq, 9) },\r | |
246 | { ORDATA (DRVN, last_drv, 3) },\r | |
247 | { FLDATA (INT, IREQ (RK), INT_V_RK) },\r | |
248 | { FLDATA (ERR, rkcs, CSR_V_ERR) },\r | |
249 | { FLDATA (DONE, rkcs, CSR_V_DONE) },\r | |
250 | { FLDATA (IE, rkcs, CSR_V_IE) },\r | |
251 | { DRDATA (STIME, rk_swait, 24), PV_LEFT },\r | |
252 | { DRDATA (RTIME, rk_rwait, 24), PV_LEFT },\r | |
253 | { FLDATA (STOP_IOE, rk_stopioe, 0) },\r | |
254 | { ORDATA (DEVADDR, rk_dib.ba, 32), REG_HRO },\r | |
255 | { ORDATA (DEVVEC, rk_dib.vec, 16), REG_HRO },\r | |
256 | { NULL }\r | |
257 | };\r | |
258 | \r | |
259 | MTAB rk_mod[] = {\r | |
260 | { UNIT_HWLK, 0, "write enabled", "WRITEENABLED", NULL },\r | |
261 | { UNIT_HWLK, UNIT_HWLK, "write locked", "LOCKED", NULL },\r | |
262 | { MTAB_XTD|MTAB_VDV, 020, "ADDRESS", "ADDRESS",\r | |
263 | &set_addr, &show_addr, NULL },\r | |
264 | { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR",\r | |
265 | &set_vec, &show_vec, NULL },\r | |
266 | { 0 }\r | |
267 | };\r | |
268 | \r | |
269 | DEVICE rk_dev = {\r | |
270 | "RK", rk_unit, rk_reg, rk_mod,\r | |
271 | RK_NUMDR, 8, 24, 1, 8, 16,\r | |
272 | NULL, NULL, &rk_reset,\r | |
273 | &rk_boot, NULL, NULL,\r | |
274 | &rk_dib, DEV_DISABLE | DEV_UBUS | DEV_Q18\r | |
275 | };\r | |
276 | \r | |
277 | /* I/O dispatch routine, I/O addresses 17777400 - 17777416\r | |
278 | \r | |
279 | 17777400 RKDS read only, constructed from "id'd drive"\r | |
280 | plus current drive status flags\r | |
281 | 17777402 RKER read only, set as operations progress,\r | |
282 | cleared by INIT or CONTROL RESET\r | |
283 | 17777404 RKCS read/write\r | |
284 | 17777406 RKWC read/write\r | |
285 | 17777410 RKBA read/write\r | |
286 | 17777412 RKDA read/write\r | |
287 | 17777414 RKMR read/write, unimplemented\r | |
288 | 17777416 RKDB read only, unimplemented\r | |
289 | */\r | |
290 | \r | |
291 | t_stat rk_rd (int32 *data, int32 PA, int32 access)\r | |
292 | {\r | |
293 | UNIT *uptr;\r | |
294 | \r | |
295 | switch ((PA >> 1) & 07) { /* decode PA<3:1> */\r | |
296 | \r | |
297 | case 0: /* RKDS: read only */\r | |
298 | rkds = (rkds & RKDS_ID) | RKDS_RK05 | RKDS_SC_OK |\r | |
299 | (rand () % RK_NUMSC); /* random sector */\r | |
300 | uptr = rk_dev.units + GET_DRIVE (rkda); /* selected unit */\r | |
301 | if (uptr->flags & UNIT_ATT) /* attached? */\r | |
302 | rkds = rkds | RKDS_RDY;\r | |
303 | if (!sim_is_active (uptr)) /* idle? */\r | |
304 | rkds = rkds | RKDS_RWS;\r | |
305 | if (uptr->flags & UNIT_WPRT) rkds = rkds | RKDS_WLK;\r | |
306 | if (GET_SECT (rkda) == (rkds & RKDS_SC)) rkds = rkds | RKDS_ON_SC;\r | |
307 | *data = rkds;\r | |
308 | return SCPE_OK;\r | |
309 | \r | |
310 | case 1: /* RKER: read only */\r | |
311 | *data = rker & RKER_IMP;\r | |
312 | return SCPE_OK;\r | |
313 | \r | |
314 | case 2: /* RKCS */\r | |
315 | rkcs = rkcs & RKCS_REAL;\r | |
316 | if (rker) rkcs = rkcs | RKCS_ERR; /* update err flags */\r | |
317 | if (rker & RKER_HARD) rkcs = rkcs | RKCS_HERR;\r | |
318 | *data = rkcs;\r | |
319 | return SCPE_OK;\r | |
320 | \r | |
321 | case 3: /* RKWC */\r | |
322 | *data = rkwc;\r | |
323 | return SCPE_OK;\r | |
324 | \r | |
325 | case 4: /* RKBA */\r | |
326 | *data = rkba & RKBA_IMP;\r | |
327 | return SCPE_OK;\r | |
328 | \r | |
329 | case 5: /* RKDA */\r | |
330 | *data = rkda;\r | |
331 | return SCPE_OK;\r | |
332 | \r | |
333 | default:\r | |
334 | *data = 0;\r | |
335 | return SCPE_OK;\r | |
336 | } /* end switch */\r | |
337 | }\r | |
338 | \r | |
339 | t_stat rk_wr (int32 data, int32 PA, int32 access)\r | |
340 | {\r | |
341 | switch ((PA >> 1) & 07) { /* decode PA<3:1> */\r | |
342 | \r | |
343 | case 0: /* RKDS: read only */\r | |
344 | return SCPE_OK;\r | |
345 | \r | |
346 | case 1: /* RKER: read only */\r | |
347 | return SCPE_OK;\r | |
348 | \r | |
349 | case 2: /* RKCS */\r | |
350 | rkcs = rkcs & RKCS_REAL;\r | |
351 | if (access == WRITEB) data = (PA & 1)?\r | |
352 | (rkcs & 0377) | (data << 8): (rkcs & ~0377) | data;\r | |
353 | if ((data & CSR_IE) == 0) { /* int disable? */\r | |
354 | rkintq = 0; /* clr int queue */\r | |
355 | CLR_INT (RK); /* clr int request */\r | |
356 | }\r | |
357 | else if ((rkcs & (CSR_DONE + CSR_IE)) == CSR_DONE) {\r | |
358 | rkintq = rkintq | RK_CTLI; /* queue ctrl int */\r | |
359 | SET_INT (RK); /* set int request */\r | |
360 | }\r | |
361 | rkcs = (rkcs & ~RKCS_RW) | (data & RKCS_RW);\r | |
362 | if ((rkcs & CSR_DONE) && (data & CSR_GO)) /* new function? */\r | |
363 | rk_go ();\r | |
364 | return SCPE_OK;\r | |
365 | \r | |
366 | case 3: /* RKWC */\r | |
367 | if (access == WRITEB) data = (PA & 1)?\r | |
368 | (rkwc & 0377) | (data << 8): (rkwc & ~0377) | data;\r | |
369 | rkwc = data;\r | |
370 | return SCPE_OK;\r | |
371 | \r | |
372 | case 4: /* RKBA */\r | |
373 | if (access == WRITEB) data = (PA & 1)?\r | |
374 | (rkba & 0377) | (data << 8): (rkba & ~0377) | data;\r | |
375 | rkba = data & RKBA_IMP;\r | |
376 | return SCPE_OK;\r | |
377 | \r | |
378 | case 5: /* RKDA */\r | |
379 | if ((rkcs & CSR_DONE) == 0) return SCPE_OK;\r | |
380 | if (access == WRITEB) data = (PA & 1)?\r | |
381 | (rkda & 0377) | (data << 8): (rkda & ~0377) | data;\r | |
382 | rkda = data;\r | |
383 | return SCPE_OK;\r | |
384 | \r | |
385 | default:\r | |
386 | return SCPE_OK;\r | |
387 | } /* end switch */\r | |
388 | }\r | |
389 | \r | |
390 | /* Initiate new function */\r | |
391 | \r | |
392 | void rk_go (void)\r | |
393 | {\r | |
394 | int32 i, sect, cyl, func;\r | |
395 | UNIT *uptr;\r | |
396 | \r | |
397 | func = GET_FUNC (rkcs); /* get function */\r | |
398 | if (func == RKCS_CTLRESET) { /* control reset? */\r | |
399 | rker = 0; /* clear errors */\r | |
400 | rkda = 0;\r | |
401 | rkba = 0;\r | |
402 | rkcs = CSR_DONE;\r | |
403 | rkintq = 0; /* clr int queue */\r | |
404 | CLR_INT (RK); /* clr int request */\r | |
405 | return;\r | |
406 | }\r | |
407 | rker = rker & ~RKER_SOFT; /* clear soft errors */\r | |
408 | if (rker == 0) rkcs = rkcs & ~RKCS_ERR; /* redo summary */\r | |
409 | rkcs = rkcs & ~RKCS_SCP; /* clear sch compl*/\r | |
410 | rk_clr_done (); /* clear done */\r | |
411 | last_drv = GET_DRIVE (rkda); /* get drive no */\r | |
412 | uptr = rk_dev.units + last_drv; /* select unit */\r | |
413 | if (uptr->flags & UNIT_DIS) { /* not present? */\r | |
414 | rk_set_done (RKER_NXD);\r | |
415 | return;\r | |
416 | }\r | |
417 | if (((uptr->flags & UNIT_ATT) == 0) || /* not att or busy? */\r | |
418 | sim_is_active (uptr)) {\r | |
419 | rk_set_done (RKER_DRE);\r | |
420 | return;\r | |
421 | }\r | |
422 | if ((rkcs & RKCS_FMT) && /* format and */\r | |
423 | (func != RKCS_READ) && (func != RKCS_WRITE)) { /* not read or write? */\r | |
424 | rk_set_done (RKER_PGE);\r | |
425 | return;\r | |
426 | }\r | |
427 | if ((func == RKCS_WRITE) && /* write and locked? */\r | |
428 | (uptr->flags & UNIT_WPRT)) {\r | |
429 | rk_set_done (RKER_WLK);\r | |
430 | return;\r | |
431 | }\r | |
432 | if (func == RKCS_WLK) { /* write lock? */\r | |
433 | uptr->flags = uptr->flags | UNIT_SWLK;\r | |
434 | rk_set_done (0);\r | |
435 | return;\r | |
436 | }\r | |
437 | if (func == RKCS_DRVRESET) { /* drive reset? */\r | |
438 | uptr->flags = uptr->flags & ~UNIT_SWLK;\r | |
439 | cyl = sect = 0;\r | |
440 | func = RKCS_SEEK;\r | |
441 | }\r | |
442 | else {\r | |
443 | sect = GET_SECT (rkda);\r | |
444 | cyl = GET_CYL (rkda);\r | |
445 | }\r | |
446 | if (sect >= RK_NUMSC) { /* bad sector? */\r | |
447 | rk_set_done (RKER_NXS);\r | |
448 | return;\r | |
449 | }\r | |
450 | if (cyl >= RK_NUMCY) { /* bad cyl? */\r | |
451 | rk_set_done (RKER_NXC);\r | |
452 | return;\r | |
453 | }\r | |
454 | i = abs (cyl - uptr->CYL) * rk_swait; /* seek time */\r | |
455 | if (func == RKCS_SEEK) { /* seek? */\r | |
456 | rk_set_done (0); /* set done */\r | |
457 | sim_activate (uptr, MAX (RK_MIN, i)); /* schedule */\r | |
458 | }\r | |
459 | else sim_activate (uptr, i + rk_rwait);\r | |
460 | uptr->FUNC = func; /* save func */\r | |
461 | uptr->CYL = cyl; /* put on cylinder */\r | |
462 | return;\r | |
463 | }\r | |
464 | \r | |
465 | /* Service unit timeout\r | |
466 | \r | |
467 | If seek in progress, complete seek command\r | |
468 | Else complete data transfer command\r | |
469 | \r | |
470 | The unit control block contains the function and disk address for\r | |
471 | the current command.\r | |
472 | */\r | |
473 | \r | |
474 | t_stat rk_svc (UNIT *uptr)\r | |
475 | {\r | |
476 | int32 i, drv, err, awc, wc, cma, cda, t;\r | |
477 | int32 da, cyl, track, sect;\r | |
478 | uint32 ma;\r | |
479 | uint16 comp;\r | |
480 | \r | |
481 | drv = (int32) (uptr - rk_dev.units); /* get drv number */\r | |
482 | if (uptr->FUNC == RKCS_SEEK) { /* seek */\r | |
483 | rkcs = rkcs | RKCS_SCP; /* set seek done */\r | |
484 | if (rkcs & CSR_IE) { /* ints enabled? */\r | |
485 | rkintq = rkintq | RK_SCPI (drv); /* queue request */\r | |
486 | if (rkcs & CSR_DONE) SET_INT (RK);\r | |
487 | }\r | |
488 | else {\r | |
489 | rkintq = 0; /* clear queue */\r | |
490 | CLR_INT (RK); /* clear interrupt */\r | |
491 | }\r | |
492 | return SCPE_OK;\r | |
493 | }\r | |
494 | \r | |
495 | if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */\r | |
496 | rk_set_done (RKER_DRE);\r | |
497 | return IORETURN (rk_stopioe, SCPE_UNATT);\r | |
498 | }\r | |
499 | sect = GET_SECT (rkda); /* get sector, cyl */\r | |
500 | cyl = GET_CYL (rkda);\r | |
501 | if (sect >= RK_NUMSC) { /* bad sector? */\r | |
502 | rk_set_done (RKER_NXS);\r | |
503 | return SCPE_OK;\r | |
504 | }\r | |
505 | if (cyl >= RK_NUMCY) { /* bad cyl? */\r | |
506 | rk_set_done (RKER_NXC);\r | |
507 | return SCPE_OK;\r | |
508 | }\r | |
509 | ma = ((rkcs & RKCS_MEX) << (16 - RKCS_V_MEX)) | rkba; /* get mem addr */\r | |
510 | da = GET_DA (rkda) * RK_NUMWD; /* get disk addr */\r | |
511 | wc = 0200000 - rkwc; /* get wd cnt */\r | |
512 | if ((da + wc) > (int32) uptr->capac) { /* overrun? */\r | |
513 | wc = uptr->capac - da; /* trim transfer */\r | |
514 | rker = rker | RKER_OVR; /* set overrun err */\r | |
515 | }\r | |
516 | \r | |
517 | err = fseek (uptr->fileref, da * sizeof (int16), SEEK_SET);\r | |
518 | if (wc && (err == 0)) { /* seek ok? */\r | |
519 | switch (uptr->FUNC) { /* case on function */\r | |
520 | \r | |
521 | case RKCS_READ: /* read */\r | |
522 | if (rkcs & RKCS_FMT) { /* format? */\r | |
523 | for (i = 0, cda = da; i < wc; i++) { /* fill buffer with cyl #s */\r | |
524 | if (cda >= (int32) uptr->capac) { /* overrun? */\r | |
525 | rker = rker | RKER_OVR; /* set overrun err */\r | |
526 | wc = i; /* trim transfer */\r | |
527 | break;\r | |
528 | }\r | |
529 | rkxb[i] = (cda / RK_NUMWD) / (RK_NUMSF * RK_NUMSC);\r | |
530 | cda = cda + RK_NUMWD; /* next sector */\r | |
531 | } /* end for wc */\r | |
532 | } /* end if format */\r | |
533 | else { /* normal read */\r | |
534 | i = fxread (rkxb, sizeof (int16), wc, uptr->fileref);\r | |
535 | err = ferror (uptr->fileref); /* read file */\r | |
536 | for ( ; i < wc; i++) rkxb[i] = 0; /* fill buf */\r | |
537 | }\r | |
538 | if (rkcs & RKCS_INH) { /* incr inhibit? */\r | |
539 | if (t = Map_WriteW (ma, 2, &rkxb[wc - 1])) { /* store last */\r | |
540 | rker = rker | RKER_NXM; /* NXM? set flag */\r | |
541 | wc = 0; /* no transfer */\r | |
542 | }\r | |
543 | }\r | |
544 | else { /* normal store */\r | |
545 | if (t = Map_WriteW (ma, wc << 1, rkxb)) { /* store buf */\r | |
546 | rker = rker | RKER_NXM; /* NXM? set flag */\r | |
547 | wc = wc - t; /* adj wd cnt */\r | |
548 | }\r | |
549 | }\r | |
550 | break; /* end read */\r | |
551 | \r | |
552 | case RKCS_WRITE: /* write */\r | |
553 | if (rkcs & RKCS_INH) { /* incr inhibit? */\r | |
554 | if (t = Map_ReadW (ma, 2, &comp)) { /* get 1st word */\r | |
555 | rker = rker | RKER_NXM; /* NXM? set flag */\r | |
556 | wc = 0; /* no transfer */\r | |
557 | }\r | |
558 | for (i = 0; i < wc; i++) rkxb[i] = comp; /* all words same */\r | |
559 | }\r | |
560 | else { /* normal fetch */\r | |
561 | if (t = Map_ReadW (ma, wc << 1, rkxb)) { /* get buf */\r | |
562 | rker = rker | RKER_NXM; /* NXM? set flg */\r | |
563 | wc = wc - t; /* adj wd cnt */\r | |
564 | }\r | |
565 | }\r | |
566 | if (wc) { /* any xfer? */\r | |
567 | awc = (wc + (RK_NUMWD - 1)) & ~(RK_NUMWD - 1); /* clr to */\r | |
568 | for (i = wc; i < awc; i++) rkxb[i] = 0; /* end of blk */\r | |
569 | fxwrite (rkxb, sizeof (int16), awc, uptr->fileref);\r | |
570 | err = ferror (uptr->fileref);\r | |
571 | }\r | |
572 | break; /* end write */\r | |
573 | \r | |
574 | case RKCS_WCHK: /* write check */\r | |
575 | i = fxread (rkxb, sizeof (int16), wc, uptr->fileref);\r | |
576 | if (err = ferror (uptr->fileref)) { /* read error? */\r | |
577 | wc = 0; /* no transfer */\r | |
578 | break;\r | |
579 | }\r | |
580 | for ( ; i < wc; i++) rkxb[i] = 0; /* fill buf */\r | |
581 | awc = wc; /* save wc */\r | |
582 | for (wc = 0, cma = ma; wc < awc; wc++) { /* loop thru buf */\r | |
583 | if (Map_ReadW (cma, 2, &comp)) { /* mem wd */\r | |
584 | rker = rker | RKER_NXM; /* NXM? set flg */\r | |
585 | break;\r | |
586 | }\r | |
587 | if (comp != rkxb[wc]) { /* match to disk? */\r | |
588 | rker = rker | RKER_WCE; /* no, err */\r | |
589 | if (rkcs & RKCS_SSE) break;\r | |
590 | }\r | |
591 | if (!(rkcs & RKCS_INH)) cma = cma + 2; /* next mem addr */\r | |
592 | } /* end for */\r | |
593 | break; /* end wcheck */\r | |
594 | \r | |
595 | default: /* read check */\r | |
596 | break;\r | |
597 | } /* end switch */\r | |
598 | } /* end else */\r | |
599 | \r | |
600 | rkwc = (rkwc + wc) & 0177777; /* final word count */\r | |
601 | if (!(rkcs & RKCS_INH)) ma = ma + (wc << 1); /* final byte addr */\r | |
602 | rkba = ma & RKBA_IMP; /* lower 16b */\r | |
603 | rkcs = (rkcs & ~RKCS_MEX) | ((ma >> (16 - RKCS_V_MEX)) & RKCS_MEX);\r | |
604 | if ((uptr->FUNC == RKCS_READ) && (rkcs & RKCS_FMT)) /* read format? */\r | |
605 | da = da + (wc * RK_NUMWD); /* count by sectors */\r | |
606 | else da = da + wc + (RK_NUMWD - 1); /* count by words */\r | |
607 | track = (da / RK_NUMWD) / RK_NUMSC;\r | |
608 | sect = (da / RK_NUMWD) % RK_NUMSC;\r | |
609 | rkda = (rkda & RKDA_DRIVE) | (track << RKDA_V_TRACK) | (sect << RKDA_V_SECT);\r | |
610 | rk_set_done (0);\r | |
611 | \r | |
612 | if (err != 0) { /* error? */\r | |
613 | perror ("RK I/O error");\r | |
614 | clearerr (uptr->fileref);\r | |
615 | return SCPE_IOERR;\r | |
616 | }\r | |
617 | return SCPE_OK;\r | |
618 | }\r | |
619 | \r | |
620 | /* Interrupt state change routines\r | |
621 | \r | |
622 | rk_set_done set done and possibly errors\r | |
623 | rk_clr_done clear done\r | |
624 | rk_inta acknowledge intererupt\r | |
625 | */\r | |
626 | \r | |
627 | void rk_set_done (int32 error)\r | |
628 | {\r | |
629 | rkcs = rkcs | CSR_DONE; /* set done */\r | |
630 | if (error != 0) {\r | |
631 | rker = rker | error; /* update error */\r | |
632 | if (rker) rkcs = rkcs | RKCS_ERR; /* update err flags */\r | |
633 | if (rker & RKER_HARD) rkcs = rkcs | RKCS_HERR;\r | |
634 | }\r | |
635 | if (rkcs & CSR_IE) { /* int enable? */\r | |
636 | rkintq = rkintq | RK_CTLI; /* set ctrl int */\r | |
637 | SET_INT (RK); /* request int */\r | |
638 | }\r | |
639 | else {\r | |
640 | rkintq = 0; /* clear queue */\r | |
641 | CLR_INT (RK);\r | |
642 | }\r | |
643 | return;\r | |
644 | }\r | |
645 | \r | |
646 | void rk_clr_done (void)\r | |
647 | {\r | |
648 | rkcs = rkcs & ~CSR_DONE; /* clear done */\r | |
649 | rkintq = rkintq & ~RK_CTLI; /* clear ctl int */\r | |
650 | CLR_INT (RK); /* clear int req */\r | |
651 | return;\r | |
652 | }\r | |
653 | \r | |
654 | int32 rk_inta (void)\r | |
655 | {\r | |
656 | int32 i;\r | |
657 | \r | |
658 | for (i = 0; i <= RK_NUMDR; i++) { /* loop thru intq */\r | |
659 | if (rkintq & (1u << i)) { /* bit i set? */\r | |
660 | rkintq = rkintq & ~(1u << i); /* clear bit i */\r | |
661 | if (rkintq) SET_INT (RK); /* queue next */\r | |
662 | rkds = (rkds & ~RKDS_ID) | /* id drive */\r | |
663 | (((i == 0)? last_drv: i - 1) << RKDS_V_ID);\r | |
664 | return rk_dib.vec; /* return vector */\r | |
665 | }\r | |
666 | }\r | |
667 | rkintq = 0; /* clear queue */\r | |
668 | return 0; /* passive release */\r | |
669 | }\r | |
670 | \r | |
671 | /* Device reset */\r | |
672 | \r | |
673 | t_stat rk_reset (DEVICE *dptr)\r | |
674 | {\r | |
675 | int32 i;\r | |
676 | UNIT *uptr;\r | |
677 | \r | |
678 | rkcs = CSR_DONE;\r | |
679 | rkda = rkba = rker = rkds = 0;\r | |
680 | rkintq = last_drv = 0;\r | |
681 | CLR_INT (RK);\r | |
682 | for (i = 0; i < RK_NUMDR; i++) {\r | |
683 | uptr = rk_dev.units + i;\r | |
684 | sim_cancel (uptr);\r | |
685 | uptr->CYL = uptr->FUNC = 0;\r | |
686 | uptr->flags = uptr->flags & ~UNIT_SWLK;\r | |
687 | }\r | |
688 | if (rkxb == NULL) rkxb = (uint16 *) calloc (RK_MAXFR, sizeof (uint16));\r | |
689 | if (rkxb == NULL) return SCPE_MEM;\r | |
690 | return SCPE_OK;\r | |
691 | }\r | |
692 | \r | |
693 | /* Device bootstrap */\r | |
694 | \r | |
695 | #define BOOT_START 02000 /* start */\r | |
696 | #define BOOT_ENTRY (BOOT_START + 002) /* entry */\r | |
697 | #define BOOT_UNIT (BOOT_START + 010) /* unit number */\r | |
698 | #define BOOT_CSR (BOOT_START + 032) /* CSR */\r | |
699 | #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16))\r | |
700 | \r | |
701 | static const uint16 boot_rom[] = {\r | |
702 | 0042113, /* "KD" */\r | |
703 | 0012706, BOOT_START, /* MOV #boot_start, SP */\r | |
704 | 0012700, 0000000, /* MOV #unit, R0 ; unit number */\r | |
705 | 0010003, /* MOV R0, R3 */\r | |
706 | 0000303, /* SWAB R3 */\r | |
707 | 0006303, /* ASL R3 */\r | |
708 | 0006303, /* ASL R3 */\r | |
709 | 0006303, /* ASL R3 */\r | |
710 | 0006303, /* ASL R3 */\r | |
711 | 0006303, /* ASL R3 */\r | |
712 | 0012701, 0177412, /* MOV #RKDA, R1 ; csr */\r | |
713 | 0010311, /* MOV R3, (R1) ; load da */\r | |
714 | 0005041, /* CLR -(R1) ; clear ba */\r | |
715 | 0012741, 0177000, /* MOV #-256.*2, -(R1) ; load wc */\r | |
716 | 0012741, 0000005, /* MOV #READ+GO, -(R1) ; read & go */\r | |
717 | 0005002, /* CLR R2 */\r | |
718 | 0005003, /* CLR R3 */\r | |
719 | 0012704, BOOT_START+020, /* MOV #START+20, R4 */\r | |
720 | 0005005, /* CLR R5 */\r | |
721 | 0105711, /* TSTB (R1) */\r | |
722 | 0100376, /* BPL .-2 */\r | |
723 | 0105011, /* CLRB (R1) */\r | |
724 | 0005007 /* CLR PC */\r | |
725 | };\r | |
726 | \r | |
727 | t_stat rk_boot (int32 unitno, DEVICE *dptr)\r | |
728 | {\r | |
729 | int32 i;\r | |
730 | extern int32 saved_PC;\r | |
731 | \r | |
732 | for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i];\r | |
733 | M[BOOT_UNIT >> 1] = unitno & RK_M_NUMDR;\r | |
734 | M[BOOT_CSR >> 1] = (rk_dib.ba & DMASK) + 012;\r | |
735 | saved_PC = BOOT_ENTRY;\r | |
736 | return SCPE_OK;\r | |
737 | }\r |