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