1 /* sds_dsk.c: SDS 940 moving head disk simulator
2 
3    Copyright (c) 2001-2008, 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    dsk          moving head disk
27 
28    The SDS 9164 disk has a subsector feature, allowing each 64W sector to be
29    viewed as 16W packets.  In addition, it has a chaining feature, allowing
30    records to be extended beyond a sector boundary.  To accomodate this, the
31    first word of each sector has 3 extra bits:
32 
33    <26> =       end of chain flag
34    <25:24> =    4 - number of packets
35 
36    These values were chosen so that 000 = continue chain, full sector.
37 */
38 
39 #include "sds_defs.h"
40 
41 #define UNIT_V_WLK      (UNIT_V_UF + 0)                 /* write locked */
42 #define UNIT_WLK        (1 << UNIT_V_WLK)
43 #define UNIT_WPRT       (UNIT_WLK | UNIT_RO)            /* write protect */
44 
45 #define DSK_PKTWD       16                              /* words/packet */
46 #define DSK_NUMPKT      4                               /* packets/sector */
47 #define DSK_NUMWD       (DSK_PKTWD*DSK_NUMPKT)          /* words/sector */
48 #define DSK_N_SC        5                               /* sect addr width */
49 #define DSK_V_SC        0                               /* position */
50 #define DSK_M_SC        ((1 << DSK_N_SC) - 1)           /* mask */
51 #define DSK_NUMSC       (1 << DSK_N_SC)                 /* sectors/track */
52 #define DSK_N_TR        8                               /* track addr width */
53 #define DSK_V_TR        (DSK_N_SC)                      /* position */
54 #define DSK_M_TR        ((1 << DSK_N_TR) - 1)           /* mask */
55 #define DSK_NUMTR       (1 << DSK_N_TR)                 /* tracks/surface */
56 #define DSK_N_SF        5                               /* surf addr width */
57 #define DSK_V_SF        (DSK_N_SC + DSK_N_TR)           /* position */
58 #define DSK_M_SF        ((1 << DSK_N_SF) - 1)           /* mask */
59 #define DSK_NUMSF       (1 << DSK_N_SF)                 /* surfaces/drive */
60 #define DSK_SCSIZE      (DSK_NUMSF*DSK_NUMTR*DSK_NUMSC) /* sectors/drive */
61 #define DSK_AMASK       (DSK_SCSIZE - 1)                /* address mask */
62 #define DSK_SIZE        (DSK_SCSIZE * DSK_NUMWD)        /* words/drive */
63 #define DSK_GETTR(x)    (((x) >> DSK_V_TR) & DSK_M_TR)
64 #define cyl             u3                              /* curr cylinder */
65 #define DSK_SIP         (1 << (DSK_N_TR + 2))
66 #define DSK_V_PKT       24
67 #define DSK_M_PKT       03
68 #define DSK_V_CHN       26
69 #define DSK_GETPKT(x)   (4 - (((x) >> DSK_V_PKT) & DSK_M_PKT))
70 #define DSK_ENDCHN(x)   ((x) & (1 << DSK_V_CHN))
71 
72 extern uint32 xfr_req;
73 extern uint32 alert;
74 extern int32 stop_invins, stop_invdev, stop_inviop;
75 int32 dsk_da = 0;                                       /* disk addr */
76 int32 dsk_op = 0;                                       /* operation */
77 int32 dsk_err = 0;                                      /* error flag */
78 uint32 dsk_buf[DSK_NUMWD];                              /* sector buf */
79 int32 dsk_bptr = 0;                                     /* byte ptr */
80 int32 dsk_blnt = 0;                                     /* byte lnt */
81 int32 dsk_time = 5;                                     /* time per char */
82 int32 dsk_stime = 200;                                  /* seek time */
83 int32 dsk_stopioe = 1;
84 DSPT dsk_tplt[] = {                                     /* template */
85     { 1, 0 },
86     { 1, DEV_OUT },
87     { 0, 0 }
88     };
89 
90 DEVICE dsk_dev;
91 t_stat dsk_svc (UNIT *uptr);
92 t_stat dsk_reset (DEVICE *dptr);
93 t_stat dsk_fill (uint32 dev);
94 t_stat dsk_read_buf (uint32 dev);
95 t_stat dsk_write_buf (uint32 dev);
96 void dsk_end_op (uint32 fl);
97 t_stat dsk (uint32 fnc, uint32 inst, uint32 *dat);
98 
99 /* DSK data structures
100 
101    dsk_dev      device descriptor
102    dsk_unit     unit descriptor
103    dsk_reg      register list
104 */
105 
106 DIB dsk_dib = { CHAN_F, DEV_DSK, XFR_DSK, dsk_tplt, &dsk };
107 
108 UNIT dsk_unit = { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE, DSK_SIZE) };
109 
110 REG dsk_reg[] = {
111     { BRDATA (BUF, dsk_buf, 8, 24, DSK_NUMWD) },
112     { DRDATA (BPTR, dsk_bptr, 9), PV_LEFT },
113     { DRDATA (BLNT, dsk_bptr, 9), PV_LEFT },
114     { ORDATA (DA, dsk_da, 21) },
115     { ORDATA (INST, dsk_op, 24) },
116     { FLDATA (XFR, xfr_req, XFR_V_DSK) },
117     { FLDATA (ERR, dsk_err, 0) },
118     { DRDATA (WTIME, dsk_time, 24), REG_NZ + PV_LEFT },
119     { DRDATA (STIME, dsk_stime,24), REG_NZ + PV_LEFT },
120     { FLDATA (STOP_IOE, dsk_stopioe, 0) },
121     { NULL }
122     };
123 
124 MTAB dsk_mod[] = {
125     { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },
126     { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },
127     { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL",
128       &set_chan, &show_chan, NULL },
129     { 0 }
130     };
131 
132 DEVICE dsk_dev = {
133     "DSK", &dsk_unit, dsk_reg, dsk_mod,
134     1, 8, 24, 1, 8, 27,
135     NULL, NULL, &dsk_reset,
136     NULL, NULL, NULL,
137     &dsk_dib, DEV_DISABLE
138     };
139 
140 /* Moving head disk routine
141 
142    conn -       inst = EOM0, dat = NULL
143    eom1 -       inst = EOM1, dat = NULL
144    sks -        inst = SKS, dat = ptr to result
145    disc -       inst = device number, dat = NULL
146    wreor -      inst = device number, dat = NULL
147    read -       inst = device number, dat = ptr to data
148    write -      inst = device number, dat = ptr to result
149 */
150 
dsk(uint32 fnc,uint32 inst,uint32 * dat)151 t_stat dsk (uint32 fnc, uint32 inst, uint32 *dat)
152 {
153 int32 i, t, new_ch, dsk_wptr, dsk_byte;
154 t_stat r;
155 
156 switch (fnc) {                                          /* case on function */
157 
158     case IO_CONN:                                       /* connect */
159         new_ch = I_GETEOCH (inst);                      /* get new chan */
160         if (new_ch != dsk_dib.chan)                     /* wrong chan? */
161             return SCPE_IERR;
162         dsk_op = inst;                                  /* save instr */
163         dsk_bptr = dsk_blnt = 0;                        /* init ptrs */
164         for (i = 0; i < DSK_NUMWD; i++)                 /* clear buffer */
165             dsk_buf[i] = 0;
166         xfr_req = xfr_req & ~XFR_DSK;                   /* clr xfr flg */
167         sim_activate (&dsk_unit, dsk_stime);            /* activate */
168         break;
169 
170     case IO_EOM1:                                       /* EOM mode 1 */
171         new_ch = I_GETEOCH (inst);                      /* get new chan */
172         if (new_ch != dsk_dib.chan)                     /* wrong chan? */
173             return SCPE_IERR;
174         if (inst & 07600)                               /* inv inst? */
175             CRETIOP;
176         alert = POT_DSK;                                /* alert */
177         break;
178 
179     case IO_DISC:                                       /* disconnect */
180         dsk_end_op (0);                                 /* normal term */
181         if (inst & DEV_OUT)
182             return dsk_fill (inst);                     /* fill write */
183         break;
184 
185     case IO_WREOR:                                      /* write eor */
186         dsk_end_op (CHF_EOR);                           /* eor term */
187         return dsk_fill (inst);                         /* fill write */
188 
189     case IO_SKS:                                        /* SKS */
190         new_ch = I_GETSKCH (inst);                      /* sks chan */
191         if (new_ch != dsk_dib.chan)
192             return SCPE_IERR;   /* wrong chan? */
193         t = I_GETSKCND (inst);                          /* sks cond */
194         if (((t == 000) && !sim_is_active (&dsk_unit) &&/* 10026: ready  */
195              (dsk_unit.flags & UNIT_ATT)) ||
196             ((t == 004) && !dsk_err &&                  /* 11026: !err */
197              (dsk_unit.flags & UNIT_ATT)) ||
198             ((t == 010) && ((dsk_unit.cyl & DSK_SIP) == 0)) || /* 12026: on trk */
199             ((t == 014) && !(dsk_unit.flags & UNIT_WPRT)) ||   /* 13026: !wrprot */
200             ((t == 001) && (dsk_unit.flags & UNIT_ATT)))       /* 10226: online */
201             *dat = 1;
202         break;
203 
204     case IO_READ:
205         xfr_req = xfr_req & ~XFR_DSK;                   /* clr xfr req */
206         if (dsk_bptr >= dsk_blnt) {                     /* no more data? */
207             if ((r = dsk_read_buf (inst)))              /* read sector */
208                 return r;
209             }
210         dsk_wptr = dsk_bptr >> 2;                       /* word pointer */
211         dsk_byte = dsk_bptr & 03;                       /* byte in word */
212         *dat = (dsk_buf[dsk_wptr] >> ((3 - dsk_byte) * 6)) & 077;
213         dsk_bptr = dsk_bptr + 1;                        /* incr buf ptr */
214         if ((dsk_bptr >= dsk_blnt) &&                   /* end sector, */
215             ((dsk_op & CHC_BIN) || DSK_ENDCHN (dsk_buf[0])))/* sec mode | eoch? */
216             dsk_end_op (CHF_EOR);                       /* eor term */
217         break;
218 
219     case IO_WRITE:
220         xfr_req = xfr_req & ~XFR_DSK;                   /* clr xfr req */
221         if (dsk_bptr >= (DSK_NUMWD * 4)) {              /* full? */
222             if ((r = dsk_write_buf (inst)))             /* write sector */
223                 return r;
224             }
225         dsk_wptr = dsk_bptr >> 2;                       /* word pointer */
226         dsk_buf[dsk_wptr] = ((dsk_buf[dsk_wptr] << 6) | (*dat & 077)) & DMASK;
227         dsk_bptr = dsk_bptr + 1;                        /* incr buf ptr */
228         break;
229 
230     default:
231         CRETINS;
232         }
233 
234 return SCPE_OK;
235 }
236 
237 /* PIN routine - return disk address */
238 
pin_dsk(uint32 num,uint32 * dat)239 t_stat pin_dsk (uint32 num, uint32 *dat)
240 {
241 *dat = dsk_da;                                          /* ret disk addr */
242 return SCPE_OK;
243 }
244 
245 /* POT routine - start seek */
246 
pot_dsk(uint32 num,uint32 * dat)247 t_stat pot_dsk (uint32 num, uint32 *dat)
248 {
249 int32 st;
250 
251 if (sim_is_active (&dsk_unit))                          /* busy? wait */
252     return STOP_IONRDY;
253 dsk_da = (*dat) & DSK_AMASK;                            /* save dsk addr */
254 st = abs (DSK_GETTR (dsk_da) -                          /* calc seek time */
255      (dsk_unit.cyl & DSK_M_TR)) * dsk_stime;
256 if (st == 0                                             /* min time */
257         ) st = dsk_stime;
258 sim_activate (&dsk_unit, st);                           /* set timer */
259 dsk_unit.cyl = dsk_unit.cyl | DSK_SIP;                  /* seeking */
260 return SCPE_OK;
261 }
262 
263 /* Unit service and read/write */
264 
dsk_svc(UNIT * uptr)265 t_stat dsk_svc (UNIT *uptr)
266 {
267 if (uptr->cyl & DSK_SIP) {                              /* end seek? */
268     uptr->cyl = DSK_GETTR (dsk_da);                     /* on cylinder */
269     if (dsk_op)                                         /* sched r/w */
270         sim_activate (&dsk_unit, dsk_stime);
271     }
272 else {
273     xfr_req = xfr_req | XFR_DSK;                        /* set xfr req */
274     sim_activate (&dsk_unit, dsk_time);                 /* activate */
275     }
276 return SCPE_OK;
277 }
278 
279 /* Read sector */
280 
dsk_read_buf(uint32 dev)281 t_stat dsk_read_buf (uint32 dev)
282 {
283 int32 da, pkts, awc;
284 
285 if ((dsk_unit.flags & UNIT_ATT) == 0) {                 /* !attached? */
286     dsk_end_op (CHF_ERR | CHF_EOR);                     /* disk error */
287     CRETIOE (dsk_stopioe, SCPE_UNATT);
288     }
289 da = dsk_da * DSK_NUMWD * sizeof (uint32);
290 fseek (dsk_unit.fileref, da, SEEK_SET);                 /* locate sector */
291 awc = fxread (dsk_buf, sizeof (uint32), DSK_NUMWD, dsk_unit.fileref);
292 if (ferror (dsk_unit.fileref)) {                        /* error? */
293     dsk_end_op (CHF_ERR | CHF_EOR);                     /* disk error */
294     return SCPE_IOERR;
295     }
296 for ( ; awc < DSK_NUMWD; awc++)
297     dsk_buf[awc] = 0;
298 pkts = DSK_GETPKT (dsk_buf[0]);                         /* get packets */
299 dsk_blnt = pkts * DSK_PKTWD * 4;                        /* new buf size */
300 dsk_bptr = 0;                                           /* init bptr */
301 dsk_da = (dsk_da + 1) & DSK_AMASK;                      /* incr disk addr */
302 return SCPE_OK;
303 }
304 
305 /* Write sector.  If this routine is called directly, then the sector
306    buffer is full, and there is at least one more character to write;
307    therefore, there are 4 packets in the sector, and the sector is not
308    the end of the chain.
309 */
310 
dsk_write_buf(uint32 dev)311 t_stat dsk_write_buf (uint32 dev)
312 {
313 int32 i, da;
314 
315 if ((dsk_unit.flags & UNIT_ATT) == 0) {                 /* !attached? */
316     dsk_end_op (CHF_ERR | CHF_EOR);                     /* disk error */
317     CRETIOE (dsk_stopioe, SCPE_UNATT);
318     }
319 if (dsk_unit.flags & UNIT_WPRT) {                       /* write prot? */
320     dsk_end_op (CHF_ERR | CHF_EOR);                     /* disk error */
321     return SCPE_OK;
322     }
323 da = dsk_da * DSK_NUMWD * sizeof (uint32);
324 fseek (dsk_unit.fileref, da, SEEK_SET);                 /* locate sector */
325 fxwrite (dsk_buf, sizeof (uint32), DSK_NUMWD, dsk_unit.fileref);
326 if (ferror (dsk_unit.fileref)) {                        /* error? */
327     dsk_end_op (CHF_ERR | CHF_EOR);                     /* disk error */
328     return SCPE_IOERR;
329     }
330 dsk_bptr = 0;                                           /* init bptr */
331 dsk_da = (dsk_da + 1) & DSK_AMASK;                      /* incr disk addr */
332 for (i = 0; i < DSK_NUMWD; i++)                         /* clear buffer */
333     dsk_buf[i] = 0;
334 return SCPE_OK;
335 }
336 
337 /* Fill incomplete sector at end of operation.  Calculate the number
338    of packets and set the end of chain flag.
339 */
340 
dsk_fill(uint32 dev)341 t_stat dsk_fill (uint32 dev)
342 {
343 int32 nochn = (dsk_op & CHC_BIN)? 0: 1;                 /* chain? */
344 int32 pktend = (dsk_bptr + ((DSK_PKTWD * 4) - 1)) &     /* end pkt */
345     ~((DSK_PKTWD * 4) - 1);
346 int32 pkts = pktend / (DSK_PKTWD * 4);                  /* # packets */
347 
348 if (dsk_bptr == 0)                                      /* no fill? */
349     return SCPE_OK;
350 for ( ; dsk_bptr < pktend; dsk_bptr++) {                /* fill packet */
351     int32 dsk_wptr = dsk_bptr >> 2;
352     dsk_buf[dsk_wptr] = (dsk_buf[dsk_wptr] << 6) & DMASK;
353     }
354 dsk_buf[0] = dsk_buf[0] | (nochn << DSK_V_CHN) |        /* insert chain, */
355     ((4 - pkts) << DSK_V_PKT);                          /* num pkts */
356 return dsk_write_buf (dev);                             /* write sec */
357 }
358 
359 /* Terminate DSK operation */
360 
dsk_end_op(uint32 fl)361 void dsk_end_op (uint32 fl)
362 {
363 if (fl)                                                 /* set flags */
364     chan_set_flag (dsk_dib.chan, fl);
365 dsk_op = 0;                                             /* clear op */
366 xfr_req = xfr_req & ~XFR_DSK;                           /* clear xfr */
367 sim_cancel (&dsk_unit);                                 /* stop */
368 if (fl & CHF_ERR) {                                     /* error? */
369     chan_disc (dsk_dib.chan);                           /* disconnect */
370     dsk_err = 1;                                        /* set disk err */
371     }
372 return;
373 }
374 
375 /* Reset routine */
376 
dsk_reset(DEVICE * dptr)377 t_stat dsk_reset (DEVICE *dptr)
378 {
379 int32 i;
380 
381 chan_disc (dsk_dib.chan);                               /* disconnect */
382 dsk_da = 0;                                             /* clear state */
383 dsk_op = 0;
384 dsk_err = 0;
385 dsk_bptr = dsk_blnt = 0;
386 xfr_req = xfr_req & ~XFR_DSK;                           /* clr xfr req */
387 sim_cancel (&dsk_unit);                                 /* deactivate */
388 dsk_unit.cyl = 0;
389 for (i = 0; i < DSK_NUMWD; i++)                         /* clear buffer */
390     dsk_buf[i] = 0;
391 return SCPE_OK;
392 }
393