1
/* RK11 cartridge disk simulator
3
Copyright (c) 1993-1997,
4
Robert M Supnik, Digital Equipment Corporation
5
Commercial use prohibited
7
29-Jun-96 RMS Added unit disable support.
9
The RK11 is an eight drive cartridge disk subsystem. An RK05 drive
10
consists of 203 cylinders, each with 2 surfaces containing 12 sectors
13
The most complicated part of the RK11 controller is the concept of
14
interrupt "polling". While only one read or write can occur at a
15
time, the controller supports multiple seeks. When a seek completes,
16
if done is set the drive attempts to interrupt. If an interrupt is
17
already pending, the interrupt is "queued" until it can be processed.
18
When an interrupt occurs, RKDS<15:13> is loaded with the number of the
21
To implement this structure, and to assure that read/write interrupts
22
take priority over seek interrupts, the controller contains an
23
interrupt queue, rkintq, with a bit for a controller interrupt and
24
then one for each drive. In addition, the drive number of the last
25
non-seeking drive is recorded in last_drv.
28
#include "pdp11_defs.h"
32
#define RK_NUMWD 256 /* words/sector */
33
#define RK_NUMSC 12 /* sectors/surface */
34
#define RK_NUMSF 2 /* surfaces/cylinder */
35
#define RK_NUMCY 203 /* cylinders/drive */
36
#define RK_NUMTR (RK_NUMCY * RK_NUMSF) /* tracks/drive */
37
#define RK_NUMDR 8 /* drives/controller */
39
#define RK_SIZE (RK_NUMCY * RK_NUMSF * RK_NUMSC * RK_NUMWD) /* words/drive */
40
#define RK_MAXMEM ((int) (MEMSIZE / sizeof (int16))) /* words/memory */
41
#define RK_CTLI 1 /* controller int */
42
#define RK_SCPI(x) (2u << (x)) /* drive int */
44
/* Flags in the unit flags word */
46
#define UNIT_V_HWLK (UNIT_V_UF + 0) /* hwre write lock */
47
#define UNIT_V_SWLK (UNIT_V_UF + 1) /* swre write lock */
48
#define UNIT_W_UF 3 /* user flags width */
49
#define UNIT_HWLK (1u << UNIT_V_HWLK)
50
#define UNIT_SWLK (1u << UNIT_V_SWLK)
52
/* Parameters in the unit descriptor */
54
#define CYL u3 /* current cylinder */
55
#define FUNC u4 /* function */
59
#define RKDS_SC 0000017 /* sector counter */
60
#define RKDS_ON_SC 0000020 /* on sector */
61
#define RKDS_WLK 0000040 /* write locked */
62
#define RKDS_RWS 0000100 /* rd/wr/seek ready */
63
#define RKDS_RDY 0000200 /* drive ready */
64
#define RKDS_SC_OK 0000400 /* SC valid */
65
#define RKDS_INC 0001000 /* seek incomplete */
66
#define RKDS_UNSAFE 0002000 /* unsafe */
67
#define RKDS_RK05 0004000 /* RK05 */
68
#define RKDS_PWR 0010000 /* power low */
69
#define RKDS_ID 0160000 /* drive ID */
74
#define RKER_WCE 0000001 /* write check */
75
#define RKER_CSE 0000002 /* checksum */
76
#define RKER_NXS 0000040 /* nx sector */
77
#define RKER_NXC 0000100 /* nx cylinder */
78
#define RKER_NXD 0000200 /* nx drive */
79
#define RKER_TE 0000400 /* timing error */
80
#define RKER_DLT 0001000 /* data late */
81
#define RKER_NXM 0002000 /* nx memory */
82
#define RKER_PGE 0004000 /* programming error */
83
#define RKER_SKE 0010000 /* seek error */
84
#define RKER_WLK 0020000 /* write lock */
85
#define RKER_OVR 0040000 /* overrun */
86
#define RKER_DRE 0100000 /* drive error */
87
#define RKER_IMP 0177743 /* implemented */
88
#define RKER_SOFT (RKER_WCE+RKER_CSE) /* soft errors */
89
#define RKER_HARD 0177740 /* hard errors */
93
#define RKCS_M_FUNC 0000007 /* function */
94
#define RKCS_CTLRESET 0
100
#define RKCS_DRVRESET 6
102
#define RKCS_V_FUNC 1
103
#define RKCS_MEX 0000060 /* memory extension */
105
#define RKCS_SSE 0000400 /* stop on soft err */
106
#define RKCS_FMT 0002000 /* format */
107
#define RKCS_INH 0004000 /* inhibit increment */
108
#define RKCS_SCP 0020000 /* search complete */
109
#define RKCS_HERR 0040000 /* hard error */
110
#define RKCS_ERR 0100000 /* error */
111
#define RKCS_REAL 0026776 /* kept here */
112
#define RKCS_RW 0006576 /* read/write */
113
#define GET_FUNC(x) (((x) >> RKCS_V_FUNC) & RKCS_M_FUNC)
117
#define RKDA_V_SECT 0 /* sector */
118
#define RKDA_M_SECT 017
119
#define RKDA_V_TRACK 4 /* track */
120
#define RKDA_M_TRACK 0777
121
#define RKDA_V_CYL 5 /* cylinder */
122
#define RKDA_M_CYL 0377
123
#define RKDA_V_DRIVE 13 /* drive */
124
#define RKDA_M_DRIVE 07
125
#define RKDA_DRIVE (RKDA_M_DRIVE << RKDA_V_DRIVE)
126
#define GET_SECT(x) (((x) >> RKDA_V_SECT) & RKDA_M_SECT)
127
#define GET_CYL(x) (((x) >> RKDA_V_CYL) & RKDA_M_CYL)
128
#define GET_TRACK(x) (((x) >> RKDA_V_TRACK) & RKDA_M_TRACK)
129
#define GET_DRIVE(x) (((x) >> RKDA_V_DRIVE) & RKDA_M_DRIVE)
130
#define GET_DA(x) ((GET_TRACK (x) * RK_NUMSC) + GET_SECT (x))
134
#define RKBA_IMP 0177776 /* implemented */
137
#define MAX(x,y) (((x) > (y))? (x): (y))
139
extern int32 int_req;
140
extern unsigned int16 *M; /* memory */
141
extern UNIT cpu_unit;
142
int32 rkcs = 0; /* control/status */
143
int32 rkds = 0; /* drive status */
144
int32 rkba = 0; /* memory address */
145
int32 rkda = 0; /* disk address */
146
int32 rker = 0; /* error status */
147
int32 rkwc = 0; /* word count */
148
int32 rkintq = 0; /* interrupt queue */
149
int32 last_drv = 0; /* last r/w drive */
150
int32 rk_stopioe = 1; /* stop on error */
151
int32 rk_swait = 10; /* seek time */
152
int32 rk_rwait = 10; /* rotate time */
153
t_stat rk_svc (UNIT *uptr);
154
t_stat rk_reset (DEVICE *dptr);
156
void rk_set_done (int32 error);
157
void rk_clr_done (void);
158
t_stat rk_boot (int32 unitno);
159
extern t_stat sim_activate (UNIT *uptr, int32 delay);
160
extern t_stat sim_cancel (UNIT *uptr);
161
extern int32 sim_is_active (UNIT *uptr);
162
extern size_t fxread (void *bptr, size_t size, size_t count, FILE *fptr);
163
extern size_t fxwrite (void *bptr, size_t size, size_t count, FILE *fptr);
165
/* RK11 data structures
167
rk_dev RK device descriptor
169
rk_reg RK register list
170
rk_mod RK modifier list
174
{ UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RK_SIZE) },
175
{ UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RK_SIZE) },
176
{ UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RK_SIZE) },
177
{ UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RK_SIZE) },
178
{ UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RK_SIZE) },
179
{ UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RK_SIZE) },
180
{ UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RK_SIZE) },
181
{ UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RK_SIZE) } };
184
{ ORDATA (RKCS, rkcs, 16) },
185
{ ORDATA (RKDA, rkda, 16) },
186
{ ORDATA (RKBA, rkba, 16) },
187
{ ORDATA (RKWC, rkwc, 16) },
188
{ ORDATA (RKDS, rkds, 16) },
189
{ ORDATA (RKER, rker, 16) },
190
{ ORDATA (INTQ, rkintq, 9) },
191
{ ORDATA (DRVN, last_drv, 3) },
192
{ FLDATA (INT, int_req, INT_V_RK) },
193
{ FLDATA (ERR, rkcs, CSR_V_ERR) },
194
{ FLDATA (DONE, rkcs, CSR_V_DONE) },
195
{ FLDATA (IE, rkcs, CSR_V_IE) },
196
{ DRDATA (STIME, rk_swait, 24), PV_LEFT },
197
{ DRDATA (RTIME, rk_rwait, 24), PV_LEFT },
198
{ GRDATA (FLG0, rk_unit[0].flags, 8, UNIT_W_UF, UNIT_V_UF - 1),
200
{ GRDATA (FLG1, rk_unit[1].flags, 8, UNIT_W_UF, UNIT_V_UF - 1),
202
{ GRDATA (FLG2, rk_unit[2].flags, 8, UNIT_W_UF, UNIT_V_UF - 1),
204
{ GRDATA (FLG3, rk_unit[3].flags, 8, UNIT_W_UF, UNIT_V_UF - 1),
206
{ GRDATA (FLG4, rk_unit[4].flags, 8, UNIT_W_UF, UNIT_V_UF - 1),
208
{ GRDATA (FLG5, rk_unit[5].flags, 8, UNIT_W_UF, UNIT_V_UF - 1),
210
{ GRDATA (FLG6, rk_unit[6].flags, 8, UNIT_W_UF, UNIT_V_UF - 1),
212
{ GRDATA (FLG7, rk_unit[7].flags, 8, UNIT_W_UF, UNIT_V_UF - 1),
214
{ FLDATA (STOP_IOE, rk_stopioe, 0) },
218
{ (UNIT_HWLK+UNIT_SWLK), 0, "write enabled", "ENABLED", NULL },
219
{ (UNIT_HWLK+UNIT_SWLK), UNIT_HWLK, "write locked", "LOCKED", NULL },
220
{ (UNIT_HWLK+UNIT_SWLK), UNIT_SWLK, "write locked", NULL, NULL },
221
{ (UNIT_HWLK+UNIT_SWLK), (UNIT_HWLK+UNIT_SWLK), "write locked",
226
"RK", rk_unit, rk_reg, rk_mod,
227
RK_NUMDR, 8, 24, 1, 8, 16,
228
NULL, NULL, &rk_reset,
229
&rk_boot, NULL, NULL };
231
/* I/O dispatch routine, I/O addresses 17777400 - 17777416
233
17777400 RKDS read only, constructed from "id'd drive"
234
plus current drive status flags
235
17777402 RKER read only, set as operations progress,
236
cleared by INIT or CONTROL RESET
237
17777404 RKCS read/write
238
17777406 RKWC read/write
239
17777410 RKBA read/write
240
17777412 RKDA read/write
241
17777414 RKMR read/write, unimplemented
242
17777416 RKDB read only, unimplemented
245
t_stat rk_rd (int32 *data, int32 PA, int32 access)
249
switch ((PA >> 1) & 07) { /* decode PA<3:1> */
250
case 0: /* RKDS: read only */
251
rkds = (rkds & RKDS_ID) | RKDS_RK05 | RKDS_SC_OK |
252
(rand () % RK_NUMSC); /* random sector */
253
uptr = rk_dev.units + GET_DRIVE (rkda); /* selected unit */
254
if (uptr -> flags & UNIT_ATT) rkds = rkds | RKDS_RDY; /* attached? */
255
if (!sim_is_active (uptr)) rkds = rkds | RKDS_RWS; /* idle? */
256
if (uptr -> flags & (UNIT_HWLK + UNIT_SWLK)) rkds = rkds | RKDS_WLK;
257
if (GET_SECT (rkda) == (rkds & RKDS_SC)) rkds = rkds | RKDS_ON_SC;
260
case 1: /* RKER: read only */
261
*data = rker & RKER_IMP;
264
rkcs = rkcs & RKCS_REAL;
265
if (rker) rkcs = rkcs | RKCS_ERR; /* update err flags */
266
if (rker & RKER_HARD) rkcs = rkcs | RKCS_HERR;
273
*data = rkba & RKBA_IMP;
280
return SCPE_OK; } /* end switch */
283
t_stat rk_wr (int32 data, int32 PA, int32 access)
285
switch ((PA >> 1) & 07) { /* decode PA<3:1> */
286
case 0: /* RKDS: read only */
288
case 1: /* RKER: read only */
291
rkcs = rkcs & RKCS_REAL;
292
if (access == WRITEB) data = (PA & 1)?
293
(rkcs & 0377) | (data << 8): (rkcs & ~0377) | data;
294
if ((data & CSR_IE) == 0) { /* int disable? */
295
rkintq = 0; /* clr int queue */
296
int_req = int_req & ~INT_RK; } /* clr int request */
297
else if ((rkcs & (CSR_DONE + CSR_IE)) == CSR_DONE) {
298
rkintq = rkintq | RK_CTLI; /* queue ctrl int */
299
int_req = int_req | INT_RK; } /* set int request */
300
rkcs = (rkcs & ~RKCS_RW) | (data & RKCS_RW);
301
if ((rkcs & CSR_DONE) && (data & CSR_GO)) rk_go (); /* new function? */
304
if (access == WRITEB) data = (PA & 1)?
305
(rkwc & 0377) | (data << 8): (rkwc & ~0377) | data;
309
if (access == WRITEB) data = (PA & 1)?
310
(rkba & 0377) | (data << 8): (rkba & ~0377) | data;
311
rkba = data & RKBA_IMP;
314
if ((rkcs & CSR_DONE) == 0) return SCPE_OK;
315
if (access == WRITEB) data = (PA & 1)?
316
(rkda & 0377) | (data << 8): (rkda & ~0377) | data;
320
return SCPE_OK; } /* end switch */
323
/* Initiate new function */
327
int32 i, sect, cyl, func;
330
func = GET_FUNC (rkcs); /* get function */
331
if (func == RKCS_CTLRESET) { /* control reset? */
332
rker = 0; /* clear errors */
336
rkintq = 0; /* clr int queue */
337
int_req = int_req & ~INT_RK; /* clr int request */
339
rker = rker & ~RKER_SOFT; /* clear soft errors */
340
if (rker == 0) rkcs = rkcs & ~RKCS_ERR; /* redo summary */
341
rkcs = rkcs & ~RKCS_SCP; /* clear sch compl*/
342
rk_clr_done (); /* clear done */
343
last_drv = GET_DRIVE (rkda); /* get drive no */
344
uptr = rk_dev.units + last_drv; /* select unit */
345
if (uptr -> flags & UNIT_DIS) { /* not present? */
346
rk_set_done (RKER_NXD);
348
if (((uptr -> flags & UNIT_ATT) == 0) || sim_is_active (uptr)) {
349
rk_set_done (RKER_DRE); /* not att or busy */
351
if (rkcs & (RKCS_INH + RKCS_FMT)) { /* format? */
352
rk_set_done (RKER_PGE);
354
if ((func == RKCS_WRITE) && (uptr -> flags & (UNIT_HWLK + UNIT_SWLK))) {
355
rk_set_done (RKER_WLK); /* write and locked? */
357
if (func == RKCS_WLK) { /* write lock? */
358
uptr -> flags = uptr -> flags | UNIT_SWLK;
361
if (func == RKCS_DRVRESET) { /* drive reset? */
362
uptr -> flags = uptr -> flags & ~UNIT_SWLK;
365
else { sect = GET_SECT (rkda);
366
cyl = GET_CYL (rkda); }
367
if (sect >= RK_NUMSC) { /* bad sector? */
368
rk_set_done (RKER_NXS);
370
if (cyl >= RK_NUMCY) { /* bad cyl? */
371
rk_set_done (RKER_NXC);
373
i = abs (cyl - uptr -> CYL) * rk_swait; /* seek time */
374
if (func == RKCS_SEEK) { /* seek? */
375
rk_set_done (0); /* set done */
376
sim_activate (uptr, MAX (RK_MIN, i)); } /* schedule */
377
else sim_activate (uptr, i + rk_rwait);
378
uptr -> FUNC = func; /* save func */
379
uptr -> CYL = cyl; /* put on cylinder */
383
/* Service unit timeout
385
If seek in progress, complete seek command
386
Else complete data transfer command
388
The unit control block contains the function and disk address for
392
static unsigned int16 fill[RK_NUMWD] = { 0 };
393
t_stat rk_svc (UNIT *uptr)
395
int32 comp, drv, err, awc, twc, wc;
396
int32 pa, da, fillc, track, sect;
398
drv = uptr - rk_dev.units; /* get drv number */
399
if (uptr -> FUNC == RKCS_SEEK) { /* seek */
400
rkcs = rkcs | RKCS_SCP; /* set seek done */
401
if (rkcs & CSR_IE) { /* ints enabled? */
402
rkintq = rkintq | RK_SCPI (drv); /* queue request */
403
if (rkcs & CSR_DONE) int_req = int_req | INT_RK; }
404
else { rkintq = 0; /* clear queue */
405
int_req = int_req & ~INT_RK; } /* clear interrupt */
408
if ((uptr -> flags & UNIT_ATT) == 0) { /* attached? */
409
rk_set_done (RKER_DRE);
410
return IORETURN (rk_stopioe, SCPE_UNATT); }
411
pa = (((rkcs & RKCS_MEX) << (16 - RKCS_V_MEX)) | rkba) >> 1;
412
da = GET_DA (rkda) * RK_NUMWD; /* get disk addr */
413
twc = 0200000 - rkwc; /* get true wc */
414
if ((pa + twc) > RK_MAXMEM) { /* mem overrun? */
415
rker = rker | RKER_NXM;
416
wc = (RK_MAXMEM - pa); }
418
if (wc < 0) { /* abort transfer? */
421
if ((da + twc) > RK_SIZE) { /* disk overrun? */
422
rker = rker | RKER_OVR;
423
if (wc > (RK_SIZE - da)) wc = RK_SIZE - da; }
425
err = fseek (uptr -> fileref, da * sizeof (int16), SEEK_SET);
427
if ((uptr -> FUNC == RKCS_READ) && (err == 0)) { /* read? */
428
awc = fxread (&M[pa], sizeof (int16), wc, uptr -> fileref);
429
for ( ; awc < wc; awc++) M[pa + awc] = 0;
430
err = ferror (uptr -> fileref); }
432
if ((uptr -> FUNC == RKCS_WRITE) && (err == 0)) { /* write? */
433
fxwrite (&M[pa], sizeof (int16), wc, uptr -> fileref);
434
err = ferror (uptr -> fileref);
435
if ((err == 0) && (fillc = (wc & (RK_NUMWD - 1)))) {
436
fxwrite (fill, sizeof (int16), fillc, uptr -> fileref);
437
err = ferror (uptr -> fileref); } }
439
if ((uptr -> FUNC == RKCS_WCHK) && (err == 0)) { /* write check? */
440
twc = wc; /* xfer length */
441
for (wc = 0; (err == 0) && (wc < twc); wc++) {
442
awc = fxread (&comp, sizeof (int16), 1, uptr -> fileref);
443
if (awc == 0) comp = 0;
444
if (comp != M[pa + wc]) {
445
rker = rker | RKER_WCE;
446
if (rkcs & RKCS_SSE) break; } }
447
err = ferror (uptr -> fileref); }
449
rkwc = (rkwc + wc) & 0177777; /* final word count */
450
pa = (pa + wc) << 1; /* final byte addr */
451
rkba = pa & RKBA_IMP; /* lower 16b */
452
rkcs = (rkcs & ~RKCS_MEX) | ((pa >> (16 - RKCS_V_MEX)) & RKCS_MEX);
453
da = da + wc + (RK_NUMWD - 1);
454
track = (da / RK_NUMWD) / RK_NUMSC;
455
sect = (da / RK_NUMWD) % RK_NUMSC;
456
rkda = (rkda & RKDA_DRIVE) | (track << RKDA_V_TRACK) | (sect << RKDA_V_SECT);
459
if (err != 0) { /* error? */
460
perror ("RK I/O error");
461
clearerr (uptr -> fileref);
466
/* Interrupt state change routines
468
rk_set_done set done and possibly errors
469
rk_clr_done clear done
470
rk_inta acknowledge intererupt
473
void rk_set_done (int32 error)
475
rkcs = rkcs | CSR_DONE; /* set done */
477
rker = rker | error; /* update error */
478
if (rker) rkcs = rkcs | RKCS_ERR; /* update err flags */
479
if (rker & RKER_HARD) rkcs = rkcs | RKCS_HERR; }
480
if (rkcs & CSR_IE) { /* int enable? */
481
rkintq = rkintq | RK_CTLI; /* set ctrl int */
482
int_req = int_req | INT_RK; } /* request int */
483
else { rkintq = 0; /* clear queue */
484
int_req = int_req & ~INT_RK; }
488
void rk_clr_done (void)
490
rkcs = rkcs & ~CSR_DONE; /* clear done */
491
rkintq = rkintq & ~RK_CTLI; /* clear ctl int */
492
int_req = int_req & ~INT_RK; /* clear int req */
500
for (i = 0; i <= RK_NUMDR; i++) { /* loop thru intq */
501
if (rkintq & (1u << i)) { /* bit i set? */
502
rkintq = rkintq & ~(1u << i); /* clear bit i */
503
if (rkintq) int_req = int_req | INT_RK; /* queue next */
504
rkds = (rkds & ~RKDS_ID) | /* id drive */
505
(((i == 0)? last_drv: i - 1) << RKDS_V_ID);
506
return VEC_RK; } } /* return vector */
507
rkintq = 0; /* clear queue */
508
return 0; /* passive release */
513
t_stat rk_reset (DEVICE *dptr)
519
rkda = rkba = rker = rkds = 0;
520
rkintq = last_drv = 0;
521
int_req = int_req & ~INT_RK;
522
for (i = 0; i < RK_NUMDR; i++) {
523
uptr = rk_dev.units + i;
525
uptr -> CYL = uptr -> FUNC = 0;
526
uptr -> flags = uptr -> flags & ~UNIT_SWLK; }
530
/* Device bootstrap */
532
#define BOOT_START 02000 /* start */
533
#define BOOT_UNIT 02006 /* where to store unit number */
534
#define BOOT_LEN (sizeof (boot_rom) / sizeof (int))
536
static const int32 boot_rom[] = {
537
0012706, 0002000, /* MOV #2000, SP */
538
0012700, 0000000, /* MOV #unit, R0 ; unit number */
539
0010003, /* MOV R0, R3 */
540
0000303, /* SWAB R3 */
541
0006303, /* ASL R3 */
542
0006303, /* ASL R3 */
543
0006303, /* ASL R3 */
544
0006303, /* ASL R3 */
545
0006303, /* ASL R3 */
546
0012701, 0177412, /* MOV #RKDA, R1 ; csr */
547
0010311, /* MOV R3, (R1) ; load da */
548
0005041, /* CLR -(R1) ; clear ba */
549
0012741, 0177000, /* MOV #-256.*2, -(R1) ; load wc */
550
0012741, 0000005, /* MOV #READ+GO, -(R1) ; read & go */
551
0005002, /* CLR R2 */
552
0005003, /* CLR R3 */
553
0005004, /* CLR R4 */
554
0012705, 0062153, /* MOV #"DK, R5 */
555
0105711, /* TSTB (R1) */
556
0100376, /* BPL .-2 */
557
0105011, /* CLRB (R1) */
561
t_stat rk_boot (int32 unitno)
564
extern int32 saved_PC;
566
for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i];
567
M[BOOT_UNIT >> 1] = unitno & RK_M_NUMDR;
568
saved_PC = BOOT_START;