4d5beb80870e7bb24a6adaa8792f423e247e6976
1 /* pdp8_rk.c: RK8E cartridge disk simulator
3 Copyright (c) 1993-2005, Robert M Supnik
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:
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
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.
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.
26 rk RK8E/RK05 cartridge disk
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
37 #include "pdp8_defs.h"
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)
47 #define RK_NUMDR 4 /* drives/controller */
50 /* Flags in the unit flags word */
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 */
58 /* Parameters in the unit descriptor */
60 #define CYL u3 /* current cylinder */
61 #define FUNC u4 /* function */
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)
78 /* Command register */
80 #define RKC_M_FUNC 07 /* function */
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 */
93 #define RKC_M_DRV 03 /* drive select */
95 #define RKC_CYHI 00001 /* high cylinder addr */
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))
103 #define RKD_V_SECT 0 /* sector */
104 #define RKD_M_SECT 017
105 #define RKD_V_SUR 4 /* surface */
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)
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 */
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
125 #define MAX(x,y) (((x) > (y))? (x): (y))
128 extern int32 int_req
, stop_inst
;
129 extern UNIT cpu_unit
;
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 */
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
);
146 /* RK-8E data structures
148 rk_dev RK device descriptor
150 rk_reg RK register list
151 rk_mod RK modifiers list
154 DIB rk_dib
= { DEV_RK
, 1, { &rk
} };
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
) }
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
},
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
},
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
,
199 int32
rk (int32 IR
, int32 AC
)
204 switch (IR
& 07) { /* decode IR<9:11> */
207 return (stop_inst
<< IOT_V_REASON
) + AC
;
210 return (rk_sta
& (RKS_DONE
+ RKS_ERR
))? /* skip on done, err */
214 rk_sta
= 0; /* clear status */
215 switch (AC
& 03) { /* decode AC<10:11> */
217 case RKX_CLS
: /* clear status */
218 if (rk_busy
!= 0) rk_sta
= rk_sta
| RKS_BUSY
;
219 case RKX_CLSA
: /* clear status alt */
222 case RKX_CLC
: /* clear control */
223 rk_cmd
= rk_busy
= 0; /* clear registers */
225 for (i
= 0; i
< RK_NUMDR
; i
++) sim_cancel (&rk_unit
[i
]);
228 case RKX_CLD
: /* reset drive */
229 if (rk_busy
!= 0) rk_sta
= rk_sta
| RKS_BUSY
;
230 else rk_go (RKC_SEEK
, 0); /* seek to 0 */
232 } /* end switch AC */
236 if (rk_busy
!= 0) rk_sta
= rk_sta
| RKS_BUSY
;
238 rk_da
= AC
; /* load disk addr */
239 rk_go (GET_FUNC (rk_cmd
), GET_CYL (rk_cmd
, rk_da
));
244 if (rk_busy
!= 0) rk_sta
= rk_sta
| RKS_BUSY
;
245 else rk_ma
= AC
; /* load curr addr */
249 uptr
= rk_dev
.units
+ GET_DRIVE (rk_cmd
); /* selected unit */
250 rk_sta
= rk_sta
& ~(RKS_HMOV
+ RKS_NRDY
); /* clear dynamic */
251 if ((uptr
->flags
& UNIT_ATT
) == 0) rk_sta
= rk_sta
| RKS_NRDY
;
252 if (sim_is_active (uptr
)) rk_sta
= rk_sta
| RKS_HMOV
;
256 if (rk_busy
!= 0) rk_sta
= rk_sta
| RKS_BUSY
;
258 rk_cmd
= AC
; /* load command */
259 rk_sta
= 0; /* clear status */
265 } /* end case pulse */
267 RK_INT_UPDATE
; /* update int req */
268 return 0; /* clear AC */
271 /* Initiate new function
273 Called with function, cylinder, to allow recalibrate as well as
274 load and go to be processed by this routine.
276 Assumes that the controller is idle, and that updating of interrupt
277 request will be done by the caller.
280 void rk_go (int32 func
, int32 cyl
)
285 if (func
== RKC_RALL
) func
= RKC_READ
; /* all? use standard */
286 if (func
== RKC_WALL
) func
= RKC_WRITE
;
287 uptr
= rk_dev
.units
+ GET_DRIVE (rk_cmd
); /* selected unit */
288 if ((uptr
->flags
& UNIT_ATT
) == 0) { /* not attached? */
289 rk_sta
= rk_sta
| RKS_DONE
| RKS_NRDY
| RKS_STAT
;
292 if (sim_is_active (uptr
) || (cyl
>= RK_NUMCY
)) { /* busy or bad cyl? */
293 rk_sta
= rk_sta
| RKS_DONE
| RKS_STAT
;
296 if ((func
== RKC_WRITE
) && (uptr
->flags
& UNIT_WPRT
)) {
297 rk_sta
= rk_sta
| RKS_DONE
| RKS_WLK
; /* write and locked? */
300 if (func
== RKC_WLK
) { /* write lock? */
301 uptr
->flags
= uptr
->flags
| UNIT_SWLK
;
302 rk_sta
= rk_sta
| RKS_DONE
;
305 t
= abs (cyl
- uptr
->CYL
) * rk_swait
; /* seek time */
306 if (func
== RKC_SEEK
) { /* seek? */
307 sim_activate (uptr
, MAX (RK_MIN
, t
)); /* schedule */
308 rk_sta
= rk_sta
| RKS_DONE
; /* set done */
311 sim_activate (uptr
, t
+ rk_rwait
); /* schedule */
312 rk_busy
= 1; /* set busy */
314 uptr
->FUNC
= func
; /* save func */
315 uptr
->CYL
= cyl
; /* put on cylinder */
321 If seek, complete seek command
322 Else complete data transfer command
324 The unit control block contains the function and cylinder address for
327 Note that memory addresses wrap around in the current field.
330 static uint16 fill
[RK_NUMWD
/2] = { 0 };
331 t_stat
rk_svc (UNIT
*uptr
)
333 int32 err
, wc
, wc1
, awc
, swc
, pa
, da
;
336 if (uptr
->FUNC
== RKC_SEEK
) { /* seek? */
337 seluptr
= rk_dev
.units
+ GET_DRIVE (rk_cmd
); /* see if selected */
338 if ((uptr
== seluptr
) && ((rk_cmd
& RKC_SKDN
) != 0)) {
339 rk_sta
= rk_sta
| RKS_DONE
;
345 if ((uptr
->flags
& UNIT_ATT
) == 0) { /* not att? abort */
346 rk_sta
= rk_sta
| RKS_DONE
| RKS_NRDY
| RKS_STAT
;
349 return IORETURN (rk_stopioe
, SCPE_UNATT
);
352 if ((uptr
->FUNC
== RKC_WRITE
) && (uptr
->flags
& UNIT_WPRT
)) {
353 rk_sta
= rk_sta
| RKS_DONE
| RKS_WLK
; /* write and locked? */
359 pa
= GET_MEX (rk_cmd
) | rk_ma
; /* phys address */
360 da
= GET_DA (rk_cmd
, rk_da
) * RK_NUMWD
* sizeof (int16
);/* disk address */
361 swc
= wc
= (rk_cmd
& RKC_HALF
)? RK_NUMWD
/ 2: RK_NUMWD
; /* get transfer size */
362 if ((wc1
= ((rk_ma
+ wc
) - 010000)) > 0) wc
= wc
- wc1
; /* if wrap, limit */
363 err
= fseek (uptr
->fileref
, da
, SEEK_SET
); /* locate sector */
365 if ((uptr
->FUNC
== RKC_READ
) && (err
== 0) && MEM_ADDR_OK (pa
)) { /* read? */
366 awc
= fxread (&M
[pa
], sizeof (int16
), wc
, uptr
->fileref
);
367 for ( ; awc
< wc
; awc
++) M
[pa
+ awc
] = 0; /* fill if eof */
368 err
= ferror (uptr
->fileref
);
369 if ((wc1
> 0) && (err
== 0)) { /* field wraparound? */
370 pa
= pa
& 070000; /* wrap phys addr */
371 awc
= fxread (&M
[pa
], sizeof (int16
), wc1
, uptr
->fileref
);
372 for ( ; awc
< wc1
; awc
++) M
[pa
+ awc
] = 0; /* fill if eof */
373 err
= ferror (uptr
->fileref
);
377 if ((uptr
->FUNC
== RKC_WRITE
) && (err
== 0)) { /* write? */
378 fxwrite (&M
[pa
], sizeof (int16
), wc
, uptr
->fileref
);
379 err
= ferror (uptr
->fileref
);
380 if ((wc1
> 0) && (err
== 0)) { /* field wraparound? */
381 pa
= pa
& 070000; /* wrap phys addr */
382 fxwrite (&M
[pa
], sizeof (int16
), wc1
, uptr
->fileref
);
383 err
= ferror (uptr
->fileref
);
385 if ((rk_cmd
& RKC_HALF
) && (err
== 0)) { /* fill half sector */
386 fxwrite (fill
, sizeof (int16
), RK_NUMWD
/2, uptr
->fileref
);
387 err
= ferror (uptr
->fileref
);
391 rk_ma
= (rk_ma
+ swc
) & 07777; /* incr mem addr reg */
392 rk_sta
= rk_sta
| RKS_DONE
; /* set done */
397 perror ("RK I/O error");
398 clearerr (uptr
->fileref
);
406 t_stat
rk_reset (DEVICE
*dptr
)
411 rk_cmd
= rk_ma
= rk_da
= rk_sta
= rk_busy
= 0;
412 int_req
= int_req
& ~INT_RK
; /* clear interrupt */
413 for (i
= 0; i
< RK_NUMDR
; i
++) { /* stop all units */
414 uptr
= rk_dev
.units
+ i
;
416 uptr
->flags
= uptr
->flags
& ~UNIT_SWLK
;
417 uptr
->CYL
= uptr
->FUNC
= 0;
422 /* Bootstrap routine */
424 #define BOOT_START 023
425 #define BOOT_UNIT 032
426 #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16))
428 static const uint16 boot_rom
[] = {
430 06744, /* 24, DLCA ; addr = 0 */
431 01032, /* 25, TAD UNIT ; unit no */
432 06746, /* 26, DLDC ; command, unit */
433 06743, /* 27, DLAG ; disk addr, go */
434 01032, /* 30, TAD UNIT ; unit no, for OS */
435 05031, /* 31, JMP . */
436 00000 /* UNIT, 0 ; in bits <9:10> */
439 t_stat
rk_boot (int32 unitno
, DEVICE
*dptr
)
442 extern int32 saved_PC
;
444 if (rk_dib
.dev
!= DEV_RK
) return STOP_NOTSTD
; /* only std devno */
445 for (i
= 0; i
< BOOT_LEN
; i
++) M
[BOOT_START
+ i
] = boot_rom
[i
];
446 M
[BOOT_UNIT
] = (unitno
& RK_M_NUMDR
) << 1;
447 saved_PC
= BOOT_START
;