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