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