1 /* h316_fhd.c: H316/516 fixed head simulator
2
3 Copyright (c) 2003-2012, 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 fhd 516-4400 fixed head disk
27
28 19-Mar-12 RMS Fixed declaration of chan_req (Mark Pizzolato)
29 15-May-06 RMS Fixed bug in autosize attach (David Gesswein)
30 04-Jan-04 RMS Changed sim_fsize calling sequence
31
32 These head-per-track devices are buffered in memory, to minimize overhead.
33 */
34
35 #include "h316_defs.h"
36 #include <math.h>
37
38 /* Constants */
39
40 #define FH_NUMWD 1536 /* words/track */
41 #define FH_NUMTK 64 /* tracks/surface */
42 #define FH_WDPSF (FH_NUMWD * FH_NUMTK) /* words/surface */
43 #define FH_NUMSF 16 /* surfaces/ctlr */
44 #define UNIT_V_AUTO (UNIT_V_UF + 0) /* autosize */
45 #define UNIT_V_SF (UNIT_V_UF + 1) /* #surfaces - 1 */
46 #define UNIT_M_SF 017
47 #define UNIT_AUTO (1 << UNIT_V_AUTO)
48 #define UNIT_SF (UNIT_M_SF << UNIT_V_SF)
49 #define UNIT_GETSF(x) ((((x) >> UNIT_V_SF) & UNIT_M_SF) + 1)
50
51 /* Command word 1 */
52
53 #define CW1_RW 0100000 /* read vs write */
54 #define CW1_V_SF 10 /* surface */
55 #define CW1_M_SF 017
56 #define CW1_GETSF(x) (((x) >> CW1_V_SF) & CW1_M_SF)
57 #define CW1_V_TK 4 /* track */
58 #define CW1_M_TK 077
59 #define CW1_GETTK(x) (((x) >> CW1_V_TK) & CW1_M_TK)
60
61 /* Command word 2 */
62
63 #define CW2_V_CA 0 /* character addr */
64 #define CW2_M_CA 07777
65 #define CW2_GETCA(x) (((x) >> CW2_V_CA) & CW2_M_CA)
66
67 #define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \
68 ((double) FH_NUMWD)))
69
70 /* OTA states */
71
72 #define OTA_NOP 0 /* normal */
73 #define OTA_CW1 1 /* expecting CW1 */
74 #define OTA_CW2 2 /* expecting CW2 */
75
76 extern int32 dev_int, dev_enb;
77 extern uint32 chan_req;
78 extern int32 stop_inst;
79 extern uint32 dma_ad[DMA_MAX];
80
81 uint32 fhd_cw1 = 0; /* cmd word 1 */
82 uint32 fhd_cw2 = 0; /* cmd word 2 */
83 uint32 fhd_buf = 0; /* buffer */
84 uint32 fhd_otas = 0; /* state */
85 uint32 fhd_busy = 0; /* busy */
86 uint32 fhd_rdy = 0; /* word ready */
87 uint32 fhd_dte = 0; /* data err */
88 uint32 fhd_ace = 0; /* access error */
89 uint32 fhd_dma = 0; /* DMA/DMC */
90 uint32 fhd_eor = 0; /* end of range */
91 uint32 fhd_csum = 0; /* parity checksum */
92 uint32 fhd_stopioe = 1; /* stop on error */
93 int32 fhd_time = 10; /* time per word */
94
95 int32 fhdio (int32 inst, int32 fnc, int32 dat, int32 dev);
96 t_stat fhd_svc (UNIT *uptr);
97 t_stat fhd_reset (DEVICE *dptr);
98 t_stat fhd_attach (UNIT *uptr, char *cptr);
99 t_stat fhd_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);
100 void fhd_go (uint32 dma);
101 void fhd_go1 (uint32 dat);
102 void fhd_go2 (uint32 dat);
103 t_bool fhd_getc (UNIT *uptr, uint32 *ch);
104 t_bool fhd_putc (UNIT *uptr, uint32 ch);
105 t_bool fhd_bad_wa (uint32 wa);
106 uint32 fhd_csword (uint32 cs, uint32 ch);
107
108 /* FHD data structures
109
110 fhd_dev device descriptor
111 fhd_unit unit descriptor
112 fhd_mod unit modifiers
113 fhd_reg register list
114 */
115
116 DIB fhd_dib = { FHD, IOBUS, 1, &fhdio };
117
118 UNIT fhd_unit = {
119 UDATA (&fhd_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF,
120 FH_WDPSF)
121 };
122
123 REG fhd_reg[] = {
124 { ORDATA (CW1, fhd_cw1, 16) },
125 { ORDATA (CW2, fhd_cw2, 16) },
126 { ORDATA (BUF, fhd_buf, 16) },
127 { FLDATA (BUSY, fhd_busy, 0) },
128 { FLDATA (RDY, fhd_rdy, 0) },
129 { FLDATA (DTE, fhd_dte, 0) },
130 { FLDATA (ACE, fhd_ace, 0) },
131 { FLDATA (EOR, fhd_eor, 0) },
132 { FLDATA (DMA, fhd_dma, 0) },
133 { FLDATA (CSUM, fhd_csum, 7) },
134 { FLDATA (INTREQ, dev_int, INT_V_MT) },
135 { FLDATA (ENABLE, dev_enb, INT_V_MT) },
136 { DRDATA (TIME, fhd_time, 31), REG_NZ + PV_LEFT },
137 { ORDATA (OTAS, fhd_otas, 2), REG_HRO },
138 { ORDATA (CHAN, fhd_dib.chan, 5), REG_HRO },
139 { FLDATA (STOP_IOE, fhd_stopioe, 0) },
140 { NULL }
141 };
142
143 MTAB fhd_mod[] = {
144 { UNIT_SF, (0 << UNIT_V_SF), NULL, "1S", &fhd_set_size },
145 { UNIT_SF, (1 << UNIT_V_SF), NULL, "2S", &fhd_set_size },
146 { UNIT_SF, (2 << UNIT_V_SF), NULL, "3S", &fhd_set_size },
147 { UNIT_SF, (3 << UNIT_V_SF), NULL, "4S", &fhd_set_size },
148 { UNIT_SF, (4 << UNIT_V_SF), NULL, "5S", &fhd_set_size },
149 { UNIT_SF, (5 << UNIT_V_SF), NULL, "6S", &fhd_set_size },
150 { UNIT_SF, (6 << UNIT_V_SF), NULL, "7S", &fhd_set_size },
151 { UNIT_SF, (7 << UNIT_V_SF), NULL, "8S", &fhd_set_size },
152 { UNIT_SF, (8 << UNIT_V_SF), NULL, "9S", &fhd_set_size },
153 { UNIT_SF, (9 << UNIT_V_SF), NULL, "10S", &fhd_set_size },
154 { UNIT_SF, (10 << UNIT_V_SF), NULL, "11S", &fhd_set_size },
155 { UNIT_SF, (11 << UNIT_V_SF), NULL, "12S", &fhd_set_size },
156 { UNIT_SF, (12 << UNIT_V_SF), NULL, "13S", &fhd_set_size },
157 { UNIT_SF, (13 << UNIT_V_SF), NULL, "14S", &fhd_set_size },
158 { UNIT_SF, (14 << UNIT_V_SF), NULL, "15S", &fhd_set_size },
159 { UNIT_SF, (15 << UNIT_V_SF), NULL, "16S", &fhd_set_size },
160 { UNIT_AUTO, UNIT_AUTO, "autosize", "AUTOSIZE", NULL },
161 { MTAB_XTD|MTAB_VDV, 0, NULL, "IOBUS",
162 &io_set_iobus, NULL, NULL },
163 { MTAB_XTD|MTAB_VDV, 0, NULL, "DMC",
164 &io_set_dmc, NULL, NULL },
165 { MTAB_XTD|MTAB_VDV, 0, NULL, "DMA",
166 &io_set_dma, NULL, NULL },
167 { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", NULL,
168 NULL, &io_show_chan, NULL },
169 { 0 }
170 };
171
172 DEVICE fhd_dev = {
173 "FHD", &fhd_unit, fhd_reg, fhd_mod,
174 1, 8, 22, 1, 8, 16,
175 NULL, NULL, &fhd_reset,
176 NULL, &fhd_attach, NULL,
177 &fhd_dib, DEV_DISABLE
178 };
179
180 /* IO routines */
181
fhdio(int32 inst,int32 fnc,int32 dat,int32 dev)182 int32 fhdio (int32 inst, int32 fnc, int32 dat, int32 dev)
183 {
184 switch (inst) { /* case on opcode */
185
186 case ioOCP: /* control */
187 if (fnc == 04) { /* terminate output? */
188 fhd_eor = 1; /* stop */
189 CLR_INT (INT_FHD); /* clear int req */
190 }
191 else if (fnc == 003) /* start, DMA */
192 fhd_go (1);
193 else if (fnc == 007) /* start, IO bus */
194 fhd_go (0);
195 else return IOBADFNC (dat);
196 break;
197
198 case ioOTA: /* output */
199 if (fnc) /* only fnc 0 */
200 return IOBADFNC (dat);
201 if (fhd_rdy) { /* ready? */
202 fhd_buf = dat; /* store data */
203 if (fhd_otas == OTA_CW1) /* expecting CW1? */
204 fhd_go1 (dat);
205 else if (fhd_otas == OTA_CW2) /* expecting CW2? */
206 fhd_go2 (dat);
207 else fhd_rdy = 0; /* normal, clr ready */
208 return IOSKIP (dat);
209 }
210 break;
211
212 case ioINA: /* input */
213 if (fnc) /* only fnc 0 */
214 return IOBADFNC (dat);
215 if (fhd_rdy) { /* ready? */
216 fhd_rdy = 0; /* clear ready */
217 return IOSKIP (dat | fhd_buf); /* return data */
218 }
219 break;
220
221 case ioSKS: /* sense */
222 if (((fnc == 000) && fhd_rdy) || /* 0 = skip if ready */
223 ((fnc == 001) && !fhd_busy) || /* 1 = skip if !busy */
224 ((fnc == 002) && !fhd_dte) || /* 2 = skip if !data err */
225 ((fnc == 003) && !fhd_ace) || /* 3 = skip if !access err */
226 ((fnc == 004) && !TST_INTREQ (INT_FHD))) /* 4 = skip if !interrupt */
227 return IOSKIP (dat);
228 break;
229
230 case ioEND:
231 fhd_eor = 1;
232 break;
233 }
234
235 return dat;
236 }
237
238 /* Start new operation */
239
fhd_go(uint32 dma)240 void fhd_go (uint32 dma)
241 {
242 int32 ch = fhd_dib.chan - 1; /* DMA/DMC chan */
243
244 if (fhd_busy) /* ignore if busy */
245 return;
246 fhd_busy = 1; /* ctlr is busy */
247 fhd_eor = 0; /* transfer not done */
248 fhd_csum = 0; /* init checksum */
249 fhd_dte = 0; /* clear errors */
250 fhd_ace = 0;
251 if (ch >= 0) /* DMA allowed? */
252 fhd_dma = dma;
253 else fhd_dma = 0; /* no, force IO bus */
254 fhd_otas = OTA_CW1; /* expect CW1 */
255 fhd_rdy = 1; /* set ready */
256 if (fhd_dma && Q_DMA (ch)) { /* DMA and DMA channel? */
257 SET_CH_REQ (ch); /* set channel request */
258 dma_ad[ch] = dma_ad[ch] & ~DMA_IN; /* force output */
259 }
260 return;
261 }
262
263 /* Process command word 1 */
264
fhd_go1(uint32 dat)265 void fhd_go1 (uint32 dat)
266 {
267 int32 ch = fhd_dib.chan - 1; /* DMA/DMC chan */
268
269 fhd_cw1 = dat; /* store CW1 */
270 fhd_otas = OTA_CW2; /* expect CW2 */
271 fhd_rdy = 1; /* set ready */
272 if (fhd_dma && Q_DMA (ch)) /* DMA? set chan request */
273 SET_CH_REQ (ch);
274 return;
275 }
276
277 /* Process command word 2 - initiate seek */
278
fhd_go2(uint32 dat)279 void fhd_go2 (uint32 dat)
280 {
281 int32 ch = fhd_dib.chan - 1; /* DMA/DMC chan */
282 uint32 sf = CW1_GETSF (fhd_cw1); /* surface */
283 int32 t, wa;
284
285 fhd_cw2 = dat; /* store CW2 */
286 fhd_otas = OTA_NOP; /* next state */
287 wa = CW2_GETCA (fhd_cw2) >> 1; /* word addr */
288 if ((wa >= FH_NUMWD) || /* if bad char addr */
289 ((fhd_unit.flags & UNIT_ATT) == 0) || /* or unattached */
290 (sf >= UNIT_GETSF (fhd_unit.flags))) { /* or bad surface */
291 fhd_ace = 1; /* access error */
292 fhd_busy = 0; /* abort operation */
293 SET_INT (INT_FHD);
294 return;
295 }
296 if (fhd_cw1 & CW1_RW) { /* write? */
297 fhd_rdy = 1; /* set ready */
298 if (fhd_dma) /* if DMA/DMC, req chan */
299 SET_CH_REQ (ch);
300 }
301 else {
302 fhd_rdy = 0; /* read, clear ready */
303 if (fhd_dma && (ch < DMC_V_DMC1)) /* read and DMA chan? */
304 dma_ad[ch] = dma_ad[ch] | DMA_IN; /* force input */
305 }
306 t = wa - GET_POS (fhd_time); /* delta to new loc */
307 if (t < 0) /* wrap around? */
308 t = t + FH_NUMWD;
309 sim_activate (&fhd_unit, t * fhd_time); /* schedule op */
310 return;
311 }
312
313 /* Unit service */
314
fhd_svc(UNIT * uptr)315 t_stat fhd_svc (UNIT *uptr)
316 {
317 int32 ch = fhd_dib.chan - 1; /* DMA/DMC chan (-1 if IO bus) */
318 uint32 c1, c2;
319
320 if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */
321 fhd_ace = 1; /* access error */
322 fhd_busy = 0; /* abort operation */
323 SET_INT (INT_FHD);
324 return IORETURN (fhd_stopioe, SCPE_UNATT);
325 }
326
327 if (fhd_eor || fhd_rdy) { /* done or ready set? */
328 if (fhd_rdy) /* if ready set, data err */
329 fhd_dte = 1;
330 if (fhd_cw1 & CW1_RW) { /* write? */
331 if (!fhd_rdy) { /* buffer full? */
332 fhd_putc (uptr, fhd_buf >> 8); /* store last word */
333 fhd_putc (uptr, fhd_buf);
334 }
335 fhd_putc (uptr, fhd_csum); /* store csum */
336 }
337 else { /* read */
338 fhd_getc (uptr, &c1); /* get csum */
339 if (fhd_csum) /* if csum != 0, err */
340 fhd_dte = 1;
341 }
342 fhd_busy = 0; /* operation complete */
343 SET_INT (INT_FHD);
344 return SCPE_OK;
345 }
346
347 if (fhd_cw1 & CW1_RW) { /* write? */
348 if (fhd_putc (uptr, fhd_buf >> 8))
349 return SCPE_OK;
350 if (fhd_putc (uptr, fhd_buf))
351 return SCPE_OK;
352 }
353 else { /* read */
354 if (fhd_getc (uptr, &c1))
355 return SCPE_OK;
356 if (fhd_getc (uptr, &c2))
357 return SCPE_OK;
358 fhd_buf = (c1 << 8) | c2;
359 }
360 sim_activate (uptr, fhd_time); /* next word */
361 fhd_rdy = 1; /* set ready */
362 if (fhd_dma) /* if DMA/DMC, req chan */
363 SET_CH_REQ (ch);
364 return SCPE_OK;
365 }
366
367 /* Read character from disk */
368
fhd_getc(UNIT * uptr,uint32 * ch)369 t_bool fhd_getc (UNIT *uptr, uint32 *ch)
370 {
371 uint32 sf = CW1_GETSF (fhd_cw1); /* surface */
372 uint32 tk = CW1_GETTK (fhd_cw1); /* track */
373 uint32 ca = CW2_GETCA (fhd_cw2); /* char addr */
374 uint32 wa = ca >> 1; /* word addr */
375 uint32 ba = (((sf * FH_NUMTK) + tk) * FH_NUMWD) + wa; /* buffer offset */
376 uint16 *fbuf = uptr->filebuf; /* buffer base */
377 uint32 wd;
378
379 if (fhd_bad_wa (wa)) /* addr bad? */
380 return TRUE;
381 fhd_cw2 = fhd_cw2 + 1; /* incr char addr */
382 if (ca & 1) /* select char */
383 wd = fbuf[ba] & 0377;
384 else wd = (fbuf[ba] >> 8) & 0377;
385 fhd_csum = fhd_csword (fhd_csum, wd); /* put in csum */
386 *ch = wd; /* return */
387 return FALSE;
388 }
389
390 /* Write character to disk */
391
fhd_putc(UNIT * uptr,uint32 ch)392 t_bool fhd_putc (UNIT *uptr, uint32 ch)
393 {
394 uint32 sf = CW1_GETSF (fhd_cw1); /* surface */
395 uint32 tk = CW1_GETTK (fhd_cw1); /* track */
396 uint32 ca = CW2_GETCA (fhd_cw2); /* char addr */
397 uint32 wa = ca >> 1; /* word addr */
398 uint32 ba = (((sf * FH_NUMTK) + tk) * FH_NUMWD) + wa; /* buffer offset */
399 uint16 *fbuf = uptr->filebuf; /* buffer base */
400
401 ch = ch & 0377; /* mask char */
402 if (fhd_bad_wa (wa)) /* addr bad? */
403 return TRUE;
404 fhd_cw2 = fhd_cw2 + 1; /* incr char addr */
405 if (ca & 1) /* odd? low char */
406 fbuf[ba] = (fbuf[ba] & ~0377) | ch;
407 else fbuf[ba] = (fbuf[ba] & 0377) | (ch << 8); /* even, hi char */
408 fhd_csum = fhd_csword (fhd_csum, ch); /* put in csum */
409 if (ba >= uptr->hwmark) /* update hwmark */
410 uptr->hwmark = ba + 1;
411 return FALSE;
412 }
413
414 /* Check word address */
415
fhd_bad_wa(uint32 wa)416 t_bool fhd_bad_wa (uint32 wa)
417 {
418 if (wa >= FH_NUMWD) { /* bad address? */
419 fhd_ace = 1; /* access error */
420 fhd_busy = 0; /* abort operation */
421 SET_INT (INT_FHD);
422 return TRUE;
423 }
424 return FALSE;
425 }
426
427 /* Add character to checksum (parity) */
428
fhd_csword(uint32 cs,uint32 ch)429 uint32 fhd_csword (uint32 cs, uint32 ch)
430 {
431 while (ch) { /* count bits */
432 ch = ch & ~(ch & (-(int32) ch));
433 cs = cs ^ 0200; /* invert cs for each 1 */
434 }
435 return cs;
436 }
437
438 /* Reset routine */
439
fhd_reset(DEVICE * dptr)440 t_stat fhd_reset (DEVICE *dptr)
441 {
442 fhd_busy = 0; /* reset state */
443 fhd_rdy = 0;
444 fhd_ace = 0;
445 fhd_dte = 0;
446 fhd_eor = 0;
447 fhd_otas = OTA_NOP;
448 fhd_cw1 = fhd_cw2 = fhd_buf = 0;
449 CLR_INT (INT_FHD); /* clear int, enb */
450 CLR_ENB (INT_FHD);
451 sim_cancel (&fhd_unit); /* cancel operation */
452 return SCPE_OK;
453 }
454
455 /* Attach routine */
456
fhd_attach(UNIT * uptr,char * cptr)457 t_stat fhd_attach (UNIT *uptr, char *cptr)
458 {
459 uint32 sz, sf;
460 uint32 ds_bytes = FH_WDPSF * sizeof (int16);
461
462 if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) {
463 sf = (sz + ds_bytes - 1) / ds_bytes;
464 if (sf >= FH_NUMSF)
465 sf = FH_NUMSF - 1;
466 uptr->flags = (uptr->flags & ~UNIT_SF) |
467 (sf << UNIT_V_SF);
468 }
469 uptr->capac = UNIT_GETSF (uptr->flags) * FH_WDPSF;
470 return attach_unit (uptr, cptr);
471 }
472
473 /* Set size routine */
474
fhd_set_size(UNIT * uptr,int32 val,char * cptr,void * desc)475 t_stat fhd_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)
476 {
477 if (val < 0)
478 return SCPE_IERR;
479 if (uptr->flags & UNIT_ATT)
480 return SCPE_ALATT;
481 uptr->capac = UNIT_GETSF (val) * FH_WDPSF;
482 return SCPE_OK;
483 }
484