1 /* pdp8_rk.c: RK8E cartridge disk simulator
2
3 Copyright (c) 1993-2011, 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 rk RK8E/RK05 cartridge disk
27
28 25-Apr-03 RMS Revised for extended file support
29 04-Oct-02 RMS Added DIB, device number support
30 06-Jan-02 RMS Changed enable/disable support
31 30-Nov-01 RMS Added read only unit, extended SET/SHOW support
32 24-Nov-01 RMS Converted FLG to array, made register names consistent
33 25-Apr-01 RMS Added device enable/disable support
34 29-Jun-96 RMS Added unit enable/disable support
35 */
36
37 #include "pdp8_defs.h"
38
39 /* Constants */
40
41 #define RK_NUMSC 16 /* sectors/surface */
42 #define RK_NUMSF 2 /* surfaces/cylinder */
43 #define RK_NUMCY 203 /* cylinders/drive */
44 #define RK_NUMWD 256 /* words/sector */
45 #define RK_SIZE (RK_NUMCY * RK_NUMSF * RK_NUMSC * RK_NUMWD)
46 /* words/drive */
47 #define RK_NUMDR 4 /* drives/controller */
48 #define RK_M_NUMDR 03
49
50 /* Flags in the unit flags word */
51
52 #define UNIT_V_HWLK (UNIT_V_UF + 0) /* hwre write lock */
53 #define UNIT_V_SWLK (UNIT_V_UF + 1) /* swre write lock */
54 #define UNIT_HWLK (1 << UNIT_V_HWLK)
55 #define UNIT_SWLK (1 << UNIT_V_SWLK)
56 #define UNIT_WPRT (UNIT_HWLK|UNIT_SWLK|UNIT_RO) /* write protect */
57
58 /* Parameters in the unit descriptor */
59
60 #define CYL u3 /* current cylinder */
61 #define FUNC u4 /* function */
62
63 /* Status register */
64
65 #define RKS_DONE 04000 /* transfer done */
66 #define RKS_HMOV 02000 /* heads moving */
67 #define RKS_SKFL 00400 /* drive seek fail */
68 #define RKS_NRDY 00200 /* drive not ready */
69 #define RKS_BUSY 00100 /* control busy error */
70 #define RKS_TMO 00040 /* timeout error */
71 #define RKS_WLK 00020 /* write lock error */
72 #define RKS_CRC 00010 /* CRC error */
73 #define RKS_DLT 00004 /* data late error */
74 #define RKS_STAT 00002 /* drive status error */
75 #define RKS_CYL 00001 /* cyl address error */
76 #define RKS_ERR (RKS_BUSY+RKS_TMO+RKS_WLK+RKS_CRC+RKS_DLT+RKS_STAT+RKS_CYL)
77
78 /* Command register */
79
80 #define RKC_M_FUNC 07 /* function */
81 #define RKC_READ 0
82 #define RKC_RALL 1
83 #define RKC_WLK 2
84 #define RKC_SEEK 3
85 #define RKC_WRITE 4
86 #define RKC_WALL 5
87 #define RKC_V_FUNC 9
88 #define RKC_IE 00400 /* interrupt enable */
89 #define RKC_SKDN 00200 /* set done on seek done */
90 #define RKC_HALF 00100 /* 128W sector */
91 #define RKC_MEX 00070 /* memory extension */
92 #define RKC_V_MEX 3
93 #define RKC_M_DRV 03 /* drive select */
94 #define RKC_V_DRV 1
95 #define RKC_CYHI 00001 /* high cylinder addr */
96
97 #define GET_FUNC(x) (((x) >> RKC_V_FUNC) & RKC_M_FUNC)
98 #define GET_DRIVE(x) (((x) >> RKC_V_DRV) & RKC_M_DRV)
99 #define GET_MEX(x) (((x) & RKC_MEX) << (12 - RKC_V_MEX))
100
101 /* Disk address */
102
103 #define RKD_V_SECT 0 /* sector */
104 #define RKD_M_SECT 017
105 #define RKD_V_SUR 4 /* surface */
106 #define RKD_M_SUR 01
107 #define RKD_V_CYL 5 /* cylinder */
108 #define RKD_M_CYL 0177
109 #define GET_CYL(x,y) ((((x) & RKC_CYHI) << (12-RKD_V_CYL)) | \
110 (((y) >> RKD_V_CYL) & RKD_M_CYL))
111 #define GET_DA(x,y) ((((x) & RKC_CYHI) << 12) | y)
112
113 /* Reset commands */
114
115 #define RKX_CLS 0 /* clear status */
116 #define RKX_CLC 1 /* clear control */
117 #define RKX_CLD 2 /* clear drive */
118 #define RKX_CLSA 3 /* clear status alt */
119
120 #define RK_INT_UPDATE if (((rk_sta & (RKS_DONE + RKS_ERR)) != 0) && \
121 ((rk_cmd & RKC_IE) != 0)) \
122 int_req = int_req | INT_RK; \
123 else int_req = int_req & ~INT_RK
124 #define RK_MIN 10
125 #define MAX(x,y) (((x) > (y))? (x): (y))
126
127 extern uint16 M[];
128 extern int32 int_req, stop_inst;
129 extern UNIT cpu_unit;
130
131 int32 rk_busy = 0; /* controller busy */
132 int32 rk_sta = 0; /* status register */
133 int32 rk_cmd = 0; /* command register */
134 int32 rk_da = 0; /* disk address */
135 int32 rk_ma = 0; /* memory address */
136 int32 rk_swait = 10, rk_rwait = 10; /* seek, rotate wait */
137 int32 rk_stopioe = 1; /* stop on error */
138
139 DEVICE rk_dev;
140 int32 rk (int32 IR, int32 AC);
141 t_stat rk_svc (UNIT *uptr);
142 t_stat rk_reset (DEVICE *dptr);
143 t_stat rk_boot (int32 unitno, DEVICE *dptr);
144 void rk_go (int32 function, int32 cylinder);
145
146 /* RK-8E data structures
147
148 rk_dev RK device descriptor
149 rk_unit RK unit list
150 rk_reg RK register list
151 rk_mod RK modifiers list
152 */
153
154 DIB rk_dib = { DEV_RK, 1, { &rk } };
155
156 UNIT rk_unit[] = {
157 { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
158 UNIT_ROABLE, RK_SIZE) },
159 { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
160 UNIT_ROABLE, RK_SIZE) },
161 { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
162 UNIT_ROABLE, RK_SIZE) },
163 { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
164 UNIT_ROABLE, RK_SIZE) }
165 };
166
167 REG rk_reg[] = {
168 { ORDATA (RKSTA, rk_sta, 12) },
169 { ORDATA (RKCMD, rk_cmd, 12) },
170 { ORDATA (RKDA, rk_da, 12) },
171 { ORDATA (RKMA, rk_ma, 12) },
172 { FLDATA (BUSY, rk_busy, 0) },
173 { FLDATA (INT, int_req, INT_V_RK) },
174 { DRDATA (STIME, rk_swait, 24), PV_LEFT },
175 { DRDATA (RTIME, rk_rwait, 24), PV_LEFT },
176 { FLDATA (STOP_IOE, rk_stopioe, 0) },
177 { ORDATA (DEVNUM, rk_dib.dev, 6), REG_HRO },
178 { NULL }
179 };
180
181 MTAB rk_mod[] = {
182 { UNIT_HWLK, 0, "write enabled", "WRITEENABLED", NULL },
183 { UNIT_HWLK, UNIT_HWLK, "write locked", "LOCKED", NULL },
184 { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
185 &set_dev, &show_dev, NULL },
186 { 0 }
187 };
188
189 DEVICE rk_dev = {
190 "RK", rk_unit, rk_reg, rk_mod,
191 RK_NUMDR, 8, 24, 1, 8, 12,
192 NULL, NULL, &rk_reset,
193 &rk_boot, NULL, NULL,
194 &rk_dib, DEV_DISABLE
195 };
196
197 /* IOT routine */
198
rk(int32 IR,int32 AC)199 int32 rk (int32 IR, int32 AC)
200 {
201 int32 i;
202 UNIT *uptr;
203
204 switch (IR & 07) { /* decode IR<9:11> */
205
206 case 0: /* unused */
207 return (stop_inst << IOT_V_REASON) + AC;
208
209 case 1: /* DSKP */
210 return (rk_sta & (RKS_DONE + RKS_ERR))? /* skip on done, err */
211 IOT_SKP + AC: AC;
212
213 case 2: /* DCLR */
214 rk_sta = 0; /* clear status */
215 switch (AC & 03) { /* decode AC<10:11> */
216
217 case RKX_CLS: /* clear status */
218 if (rk_busy != 0) rk_sta = rk_sta | RKS_BUSY;
219 case RKX_CLSA: /* clear status alt */
220 break;
221
222 case RKX_CLC: /* clear control */
223 rk_cmd = rk_busy = 0; /* clear registers */
224 rk_ma = rk_da = 0;
225 for (i = 0; i < RK_NUMDR; i++)
226 sim_cancel (&rk_unit[i]);
227 break;
228
229 case RKX_CLD: /* reset drive */
230 if (rk_busy != 0)
231 rk_sta = rk_sta | RKS_BUSY;
232 else rk_go (RKC_SEEK, 0); /* seek to 0 */
233 break;
234 } /* end switch AC */
235 break;
236
237 case 3: /* DLAG */
238 if (rk_busy != 0)
239 rk_sta = rk_sta | RKS_BUSY;
240 else {
241 rk_da = AC; /* load disk addr */
242 rk_go (GET_FUNC (rk_cmd), GET_CYL (rk_cmd, rk_da));
243 }
244 break;
245
246 case 4: /* DLCA */
247 if (rk_busy != 0)
248 rk_sta = rk_sta | RKS_BUSY;
249 else rk_ma = AC; /* load curr addr */
250 break;
251
252 case 5: /* DRST */
253 uptr = rk_dev.units + GET_DRIVE (rk_cmd); /* selected unit */
254 rk_sta = rk_sta & ~(RKS_HMOV + RKS_NRDY); /* clear dynamic */
255 if ((uptr->flags & UNIT_ATT) == 0)
256 rk_sta = rk_sta | RKS_NRDY;
257 if (sim_is_active (uptr))
258 rk_sta = rk_sta | RKS_HMOV;
259 return rk_sta;
260
261 case 6: /* DLDC */
262 if (rk_busy != 0)
263 rk_sta = rk_sta | RKS_BUSY;
264 else {
265 rk_cmd = AC; /* load command */
266 rk_sta = 0; /* clear status */
267 }
268 break;
269
270 case 7: /* DMAN */
271 break;
272 } /* end case pulse */
273
274 RK_INT_UPDATE; /* update int req */
275 return 0; /* clear AC */
276 }
277
278 /* Initiate new function
279
280 Called with function, cylinder, to allow recalibrate as well as
281 load and go to be processed by this routine.
282
283 Assumes that the controller is idle, and that updating of interrupt
284 request will be done by the caller.
285 */
286
rk_go(int32 func,int32 cyl)287 void rk_go (int32 func, int32 cyl)
288 {
289 int32 t;
290 UNIT *uptr;
291
292 if (func == RKC_RALL) /* all? use standard */
293 func = RKC_READ;
294 if (func == RKC_WALL)
295 func = RKC_WRITE;
296 uptr = rk_dev.units + GET_DRIVE (rk_cmd); /* selected unit */
297 if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */
298 rk_sta = rk_sta | RKS_DONE | RKS_NRDY | RKS_STAT;
299 return;
300 }
301 if (sim_is_active (uptr) || (cyl >= RK_NUMCY)) { /* busy or bad cyl? */
302 rk_sta = rk_sta | RKS_DONE | RKS_STAT;
303 return;
304 }
305 if ((func == RKC_WRITE) && (uptr->flags & UNIT_WPRT)) {
306 rk_sta = rk_sta | RKS_DONE | RKS_WLK; /* write and locked? */
307 return;
308 }
309 if (func == RKC_WLK) { /* write lock? */
310 uptr->flags = uptr->flags | UNIT_SWLK;
311 rk_sta = rk_sta | RKS_DONE;
312 return;
313 }
314 t = abs (cyl - uptr->CYL) * rk_swait; /* seek time */
315 if (func == RKC_SEEK) { /* seek? */
316 sim_activate (uptr, MAX (RK_MIN, t)); /* schedule */
317 rk_sta = rk_sta | RKS_DONE; /* set done */
318 }
319 else {
320 sim_activate (uptr, t + rk_rwait); /* schedule */
321 rk_busy = 1; /* set busy */
322 }
323 uptr->FUNC = func; /* save func */
324 uptr->CYL = cyl; /* put on cylinder */
325 return;
326 }
327
328 /* Unit service
329
330 If seek, complete seek command
331 Else complete data transfer command
332
333 The unit control block contains the function and cylinder address for
334 the current command.
335
336 Note that memory addresses wrap around in the current field.
337 */
338
339 static uint16 fill[RK_NUMWD/2] = { 0 };
rk_svc(UNIT * uptr)340 t_stat rk_svc (UNIT *uptr)
341 {
342 int32 err, wc, wc1, awc, swc, pa, da;
343 UNIT *seluptr;
344
345 if (uptr->FUNC == RKC_SEEK) { /* seek? */
346 seluptr = rk_dev.units + GET_DRIVE (rk_cmd); /* see if selected */
347 if ((uptr == seluptr) && ((rk_cmd & RKC_SKDN) != 0)) {
348 rk_sta = rk_sta | RKS_DONE;
349 RK_INT_UPDATE;
350 }
351 return SCPE_OK;
352 }
353
354 if ((uptr->flags & UNIT_ATT) == 0) { /* not att? abort */
355 rk_sta = rk_sta | RKS_DONE | RKS_NRDY | RKS_STAT;
356 rk_busy = 0;
357 RK_INT_UPDATE;
358 return IORETURN (rk_stopioe, SCPE_UNATT);
359 }
360
361 if ((uptr->FUNC == RKC_WRITE) && (uptr->flags & UNIT_WPRT)) {
362 rk_sta = rk_sta | RKS_DONE | RKS_WLK; /* write and locked? */
363 rk_busy = 0;
364 RK_INT_UPDATE;
365 return SCPE_OK;
366 }
367
368 pa = GET_MEX (rk_cmd) | rk_ma; /* phys address */
369 da = GET_DA (rk_cmd, rk_da) * RK_NUMWD * sizeof (int16);/* disk address */
370 swc = wc = (rk_cmd & RKC_HALF)? RK_NUMWD / 2: RK_NUMWD; /* get transfer size */
371 if ((wc1 = ((rk_ma + wc) - 010000)) > 0) /* if wrap, limit */
372 wc = wc - wc1;
373 err = fseek (uptr->fileref, da, SEEK_SET); /* locate sector */
374
375 if ((uptr->FUNC == RKC_READ) && (err == 0) && MEM_ADDR_OK (pa)) { /* read? */
376 awc = fxread (&M[pa], sizeof (int16), wc, uptr->fileref);
377 for ( ; awc < wc; awc++) /* fill if eof */
378 M[pa + awc] = 0;
379 err = ferror (uptr->fileref);
380 if ((wc1 > 0) && (err == 0)) { /* field wraparound? */
381 pa = pa & 070000; /* wrap phys addr */
382 awc = fxread (&M[pa], sizeof (int16), wc1, uptr->fileref);
383 for ( ; awc < wc1; awc++) /* fill if eof */
384 M[pa + awc] = 0;
385 err = ferror (uptr->fileref);
386 }
387 }
388
389 if ((uptr->FUNC == RKC_WRITE) && (err == 0)) { /* write? */
390 fxwrite (&M[pa], sizeof (int16), wc, uptr->fileref);
391 err = ferror (uptr->fileref);
392 if ((wc1 > 0) && (err == 0)) { /* field wraparound? */
393 pa = pa & 070000; /* wrap phys addr */
394 fxwrite (&M[pa], sizeof (int16), wc1, uptr->fileref);
395 err = ferror (uptr->fileref);
396 }
397 if ((rk_cmd & RKC_HALF) && (err == 0)) { /* fill half sector */
398 fxwrite (fill, sizeof (int16), RK_NUMWD/2, uptr->fileref);
399 err = ferror (uptr->fileref);
400 }
401 }
402
403 rk_ma = (rk_ma + swc) & 07777; /* incr mem addr reg */
404 rk_sta = rk_sta | RKS_DONE; /* set done */
405 rk_busy = 0;
406 RK_INT_UPDATE;
407
408 if (err != 0) {
409 perror ("RK I/O error");
410 clearerr (uptr->fileref);
411 return SCPE_IOERR;
412 }
413 return SCPE_OK;
414 }
415
416 /* Reset routine */
417
rk_reset(DEVICE * dptr)418 t_stat rk_reset (DEVICE *dptr)
419 {
420 int32 i;
421 UNIT *uptr;
422
423 rk_cmd = rk_ma = rk_da = rk_sta = rk_busy = 0;
424 int_req = int_req & ~INT_RK; /* clear interrupt */
425 for (i = 0; i < RK_NUMDR; i++) { /* stop all units */
426 uptr = rk_dev.units + i;
427 sim_cancel (uptr);
428 uptr->flags = uptr->flags & ~UNIT_SWLK;
429 uptr->CYL = uptr->FUNC = 0;
430 }
431 return SCPE_OK;
432 }
433
434 /* Bootstrap routine */
435
436 #define BOOT_START 023
437 #define BOOT_UNIT 032
438 #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16))
439
440 static const uint16 boot_rom[] = {
441 06007, /* 23, CAF */
442 06744, /* 24, DLCA ; addr = 0 */
443 01032, /* 25, TAD UNIT ; unit no */
444 06746, /* 26, DLDC ; command, unit */
445 06743, /* 27, DLAG ; disk addr, go */
446 01032, /* 30, TAD UNIT ; unit no, for OS */
447 05031, /* 31, JMP . */
448 00000 /* UNIT, 0 ; in bits <9:10> */
449 };
450
rk_boot(int32 unitno,DEVICE * dptr)451 t_stat rk_boot (int32 unitno, DEVICE *dptr)
452 {
453 int32 i;
454 extern int32 saved_PC;
455
456 if (rk_dib.dev != DEV_RK) /* only std devno */
457 return STOP_NOTSTD;
458 for (i = 0; i < BOOT_LEN; i++)
459 M[BOOT_START + i] = boot_rom[i];
460 M[BOOT_UNIT] = (unitno & RK_M_NUMDR) << 1;
461 saved_PC = BOOT_START;
462 return SCPE_OK;
463 }
464