f27061a5bd682983e8afac515c771d2bf9461176
1 /* h316_fhd.c: H316/516 fixed head simulator
3 Copyright (c) 2003-2006, Robert M. Supnik
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:
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
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.
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.
26 fhd 516-4400 fixed head disk
28 15-May-06 RMS Fixed bug in autosize attach (reported by David Gesswein)
29 04-Jan-04 RMS Changed sim_fsize calling sequence
31 These head-per-track devices are buffered in memory, to minimize overhead.
34 #include "h316_defs.h"
39 #define FH_NUMWD 1536 /* words/track */
40 #define FH_NUMTK 64 /* tracks/surface */
41 #define FH_WDPSF (FH_NUMWD * FH_NUMTK) /* words/surface */
42 #define FH_NUMSF 16 /* surfaces/ctlr */
43 #define UNIT_V_AUTO (UNIT_V_UF + 0) /* autosize */
44 #define UNIT_V_SF (UNIT_V_UF + 1) /* #surfaces - 1 */
46 #define UNIT_AUTO (1 << UNIT_V_AUTO)
47 #define UNIT_SF (UNIT_M_SF << UNIT_V_SF)
48 #define UNIT_GETSF(x) ((((x) >> UNIT_V_SF) & UNIT_M_SF) + 1)
52 #define CW1_RW 0100000 /* read vs write */
53 #define CW1_V_SF 10 /* surface */
55 #define CW1_GETSF(x) (((x) >> CW1_V_SF) & CW1_M_SF)
56 #define CW1_V_TK 4 /* track */
58 #define CW1_GETTK(x) (((x) >> CW1_V_TK) & CW1_M_TK)
62 #define CW2_V_CA 0 /* character addr */
63 #define CW2_M_CA 07777
64 #define CW2_GETCA(x) (((x) >> CW2_V_CA) & CW2_M_CA)
66 #define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \
71 #define OTA_NOP 0 /* normal */
72 #define OTA_CW1 1 /* expecting CW1 */
73 #define OTA_CW2 2 /* expecting CW2 */
75 extern int32 dev_int
, dev_enb
, chan_req
;
76 extern int32 stop_inst
;
77 extern uint32 dma_ad
[DMA_MAX
];
79 uint32 fhd_cw1
= 0; /* cmd word 1 */
80 uint32 fhd_cw2
= 0; /* cmd word 2 */
81 uint32 fhd_buf
= 0; /* buffer */
82 uint32 fhd_otas
= 0; /* state */
83 uint32 fhd_busy
= 0; /* busy */
84 uint32 fhd_rdy
= 0; /* word ready */
85 uint32 fhd_dte
= 0; /* data err */
86 uint32 fhd_ace
= 0; /* access error */
87 uint32 fhd_dma
= 0; /* DMA/DMC */
88 uint32 fhd_eor
= 0; /* end of range */
89 uint32 fhd_csum
= 0; /* parity checksum */
90 uint32 fhd_stopioe
= 1; /* stop on error */
91 int32 fhd_time
= 10; /* time per word */
93 int32
fhdio (int32 inst
, int32 fnc
, int32 dat
, int32 dev
);
94 t_stat
fhd_svc (UNIT
*uptr
);
95 t_stat
fhd_reset (DEVICE
*dptr
);
96 t_stat
fhd_attach (UNIT
*uptr
, char *cptr
);
97 t_stat
fhd_set_size (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
);
98 void fhd_go (uint32 dma
);
99 void fhd_go1 (uint32 dat
);
100 void fhd_go2 (uint32 dat
);
101 t_bool
fhd_getc (UNIT
*uptr
, uint32
*ch
);
102 t_bool
fhd_putc (UNIT
*uptr
, uint32 ch
);
103 t_bool
fhd_bad_wa (uint32 wa
);
104 uint32
fhd_csword (uint32 cs
, uint32 ch
);
106 /* FHD data structures
108 fhd_dev device descriptor
109 fhd_unit unit descriptor
110 fhd_mod unit modifiers
111 fhd_reg register list
114 DIB fhd_dib
= { FHD
, IOBUS
, 1, &fhdio
};
117 UDATA (&fhd_svc
, UNIT_FIX
+UNIT_ATTABLE
+UNIT_BUFABLE
+UNIT_MUSTBUF
,
122 { ORDATA (CW1
, fhd_cw1
, 16) },
123 { ORDATA (CW2
, fhd_cw2
, 16) },
124 { ORDATA (BUF
, fhd_buf
, 16) },
125 { FLDATA (BUSY
, fhd_busy
, 0) },
126 { FLDATA (RDY
, fhd_rdy
, 0) },
127 { FLDATA (DTE
, fhd_dte
, 0) },
128 { FLDATA (ACE
, fhd_ace
, 0) },
129 { FLDATA (EOR
, fhd_eor
, 0) },
130 { FLDATA (DMA
, fhd_dma
, 0) },
131 { FLDATA (CSUM
, fhd_csum
, 7) },
132 { FLDATA (INTREQ
, dev_int
, INT_V_MT
) },
133 { FLDATA (ENABLE
, dev_enb
, INT_V_MT
) },
134 { DRDATA (TIME
, fhd_time
, 31), REG_NZ
+ PV_LEFT
},
135 { ORDATA (OTAS
, fhd_otas
, 2), REG_HRO
},
136 { ORDATA (CHAN
, fhd_dib
.chan
, 5), REG_HRO
},
137 { FLDATA (STOP_IOE
, fhd_stopioe
, 0) },
142 { UNIT_SF
, (0 << UNIT_V_SF
), NULL
, "1S", &fhd_set_size
},
143 { UNIT_SF
, (1 << UNIT_V_SF
), NULL
, "2S", &fhd_set_size
},
144 { UNIT_SF
, (2 << UNIT_V_SF
), NULL
, "3S", &fhd_set_size
},
145 { UNIT_SF
, (3 << UNIT_V_SF
), NULL
, "4S", &fhd_set_size
},
146 { UNIT_SF
, (4 << UNIT_V_SF
), NULL
, "5S", &fhd_set_size
},
147 { UNIT_SF
, (5 << UNIT_V_SF
), NULL
, "6S", &fhd_set_size
},
148 { UNIT_SF
, (6 << UNIT_V_SF
), NULL
, "7S", &fhd_set_size
},
149 { UNIT_SF
, (7 << UNIT_V_SF
), NULL
, "8S", &fhd_set_size
},
150 { UNIT_SF
, (8 << UNIT_V_SF
), NULL
, "9S", &fhd_set_size
},
151 { UNIT_SF
, (9 << UNIT_V_SF
), NULL
, "10S", &fhd_set_size
},
152 { UNIT_SF
, (10 << UNIT_V_SF
), NULL
, "11S", &fhd_set_size
},
153 { UNIT_SF
, (11 << UNIT_V_SF
), NULL
, "12S", &fhd_set_size
},
154 { UNIT_SF
, (12 << UNIT_V_SF
), NULL
, "13S", &fhd_set_size
},
155 { UNIT_SF
, (13 << UNIT_V_SF
), NULL
, "14S", &fhd_set_size
},
156 { UNIT_SF
, (14 << UNIT_V_SF
), NULL
, "15S", &fhd_set_size
},
157 { UNIT_SF
, (15 << UNIT_V_SF
), NULL
, "16S", &fhd_set_size
},
158 { UNIT_AUTO
, UNIT_AUTO
, "autosize", "AUTOSIZE", NULL
},
159 { MTAB_XTD
|MTAB_VDV
, 0, NULL
, "IOBUS",
160 &io_set_iobus
, NULL
, NULL
},
161 { MTAB_XTD
|MTAB_VDV
, 0, NULL
, "DMC",
162 &io_set_dmc
, NULL
, NULL
},
163 { MTAB_XTD
|MTAB_VDV
, 0, NULL
, "DMA",
164 &io_set_dma
, NULL
, NULL
},
165 { MTAB_XTD
|MTAB_VDV
, 0, "CHANNEL", NULL
,
166 NULL
, &io_show_chan
, NULL
},
171 "FHD", &fhd_unit
, fhd_reg
, fhd_mod
,
173 NULL
, NULL
, &fhd_reset
,
174 NULL
, &fhd_attach
, NULL
,
175 &fhd_dib
, DEV_DISABLE
180 int32
fhdio (int32 inst
, int32 fnc
, int32 dat
, int32 dev
)
182 switch (inst
) { /* case on opcode */
184 case ioOCP
: /* control */
185 if (fnc
== 04) { /* terminate output? */
186 fhd_eor
= 1; /* stop */
187 CLR_INT (INT_FHD
); /* clear int req */
189 else if (fnc
== 003) fhd_go (1); /* start, DMA */
190 else if (fnc
== 007) fhd_go (0); /* start, IO bus */
191 else return IOBADFNC (dat
);
194 case ioOTA
: /* output */
195 if (fnc
) return IOBADFNC (dat
); /* only fnc 0 */
196 if (fhd_rdy
) { /* ready? */
197 fhd_buf
= dat
; /* store data */
198 if (fhd_otas
== OTA_CW1
) fhd_go1 (dat
); /* expecting CW1? */
199 else if (fhd_otas
== OTA_CW2
) fhd_go2 (dat
);/* expecting CW2? */
200 else fhd_rdy
= 0; /* normal, clr ready */
205 case ioINA
: /* input */
206 if (fnc
) return IOBADFNC (dat
); /* only fnc 0 */
207 if (fhd_rdy
) { /* ready? */
208 fhd_rdy
= 0; /* clear ready */
209 return IOSKIP (dat
| fhd_buf
); /* return data */
213 case ioSKS
: /* sense */
214 if (((fnc
== 000) && fhd_rdy
) || /* 0 = skip if ready */
215 ((fnc
== 001) && !fhd_busy
) || /* 1 = skip if !busy */
216 ((fnc
== 002) && !fhd_dte
) || /* 2 = skip if !data err */
217 ((fnc
== 003) && !fhd_ace
) || /* 3 = skip if !access err */
218 ((fnc
== 004) && !TST_INTREQ (INT_FHD
))) /* 4 = skip if !interrupt */
230 /* Start new operation */
232 void fhd_go (uint32 dma
)
234 int32 ch
= fhd_dib
.chan
- 1; /* DMA/DMC chan */
236 if (fhd_busy
) return; /* ignore if busy */
237 fhd_busy
= 1; /* ctlr is busy */
238 fhd_eor
= 0; /* transfer not done */
239 fhd_csum
= 0; /* init checksum */
240 fhd_dte
= 0; /* clear errors */
242 if (ch
>= 0) fhd_dma
= dma
; /* DMA allowed? */
243 else fhd_dma
= 0; /* no, force IO bus */
244 fhd_otas
= OTA_CW1
; /* expect CW1 */
245 fhd_rdy
= 1; /* set ready */
246 if (fhd_dma
&& Q_DMA (ch
)) { /* DMA and DMA channel? */
247 SET_CH_REQ (ch
); /* set channel request */
248 dma_ad
[ch
] = dma_ad
[ch
] & ~DMA_IN
; /* force output */
253 /* Process command word 1 */
255 void fhd_go1 (uint32 dat
)
257 int32 ch
= fhd_dib
.chan
- 1; /* DMA/DMC chan */
259 fhd_cw1
= dat
; /* store CW1 */
260 fhd_otas
= OTA_CW2
; /* expect CW2 */
261 fhd_rdy
= 1; /* set ready */
262 if (fhd_dma
&& Q_DMA (ch
)) SET_CH_REQ (ch
); /* DMA? set chan request */
266 /* Process command word 2 - initiate seek */
268 void fhd_go2 (uint32 dat
)
270 int32 ch
= fhd_dib
.chan
- 1; /* DMA/DMC chan */
271 uint32 sf
= CW1_GETSF (fhd_cw1
); /* surface */
274 fhd_cw2
= dat
; /* store CW2 */
275 fhd_otas
= OTA_NOP
; /* next state */
276 wa
= CW2_GETCA (fhd_cw2
) >> 1; /* word addr */
277 if ((wa
>= FH_NUMWD
) || /* if bad char addr */
278 ((fhd_unit
.flags
& UNIT_ATT
) == 0) || /* or unattached */
279 (sf
>= UNIT_GETSF (fhd_unit
.flags
))) { /* or bad surface */
280 fhd_ace
= 1; /* access error */
281 fhd_busy
= 0; /* abort operation */
285 if (fhd_cw1
& CW1_RW
) { /* write? */
286 fhd_rdy
= 1; /* set ready */
287 if (fhd_dma
) SET_CH_REQ (ch
); /* if DMA/DMC, req chan */
290 fhd_rdy
= 0; /* read, clear ready */
291 if (fhd_dma
&& (ch
< DMC_V_DMC1
)) /* read and DMA chan? */
292 dma_ad
[ch
] = dma_ad
[ch
] | DMA_IN
; /* force input */
294 t
= wa
- GET_POS (fhd_time
); /* delta to new loc */
295 if (t
< 0) t
= t
+ FH_NUMWD
; /* wrap around? */
296 sim_activate (&fhd_unit
, t
* fhd_time
); /* schedule op */
302 t_stat
fhd_svc (UNIT
*uptr
)
304 int32 ch
= fhd_dib
.chan
- 1; /* DMA/DMC chan (-1 if IO bus) */
307 if ((uptr
->flags
& UNIT_ATT
) == 0) { /* unattached? */
308 fhd_ace
= 1; /* access error */
309 fhd_busy
= 0; /* abort operation */
311 return IORETURN (fhd_stopioe
, SCPE_UNATT
);
314 if (fhd_eor
|| fhd_rdy
) { /* done or ready set? */
315 if (fhd_rdy
) fhd_dte
= 1; /* if ready set, data err */
316 if (fhd_cw1
& CW1_RW
) { /* write? */
317 if (!fhd_rdy
) { /* buffer full? */
318 fhd_putc (uptr
, fhd_buf
>> 8); /* store last word */
319 fhd_putc (uptr
, fhd_buf
);
321 fhd_putc (uptr
, fhd_csum
); /* store csum */
324 fhd_getc (uptr
, &c1
); /* get csum */
325 if (fhd_csum
) fhd_dte
= 1; /* if csum != 0, err */
327 fhd_busy
= 0; /* operation complete */
332 if (fhd_cw1
& CW1_RW
) { /* write? */
333 if (fhd_putc (uptr
, fhd_buf
>> 8)) return SCPE_OK
;
334 if (fhd_putc (uptr
, fhd_buf
)) return SCPE_OK
;
337 if (fhd_getc (uptr
, &c1
)) return SCPE_OK
; /* read */
338 if (fhd_getc (uptr
, &c2
)) return SCPE_OK
;
339 fhd_buf
= (c1
<< 8) | c2
;
341 sim_activate (uptr
, fhd_time
); /* next word */
342 fhd_rdy
= 1; /* set ready */
343 if (fhd_dma
) SET_CH_REQ (ch
); /* if DMA/DMC, req chan */
347 /* Read character from disk */
349 t_bool
fhd_getc (UNIT
*uptr
, uint32
*ch
)
351 uint32 sf
= CW1_GETSF (fhd_cw1
); /* surface */
352 uint32 tk
= CW1_GETTK (fhd_cw1
); /* track */
353 uint32 ca
= CW2_GETCA (fhd_cw2
); /* char addr */
354 uint32 wa
= ca
>> 1; /* word addr */
355 uint32 ba
= (((sf
* FH_NUMTK
) + tk
) * FH_NUMWD
) + wa
; /* buffer offset */
356 uint16
*fbuf
= uptr
->filebuf
; /* buffer base */
359 if (fhd_bad_wa (wa
)) return TRUE
; /* addr bad? */
360 fhd_cw2
= fhd_cw2
+ 1; /* incr char addr */
361 if (ca
& 1) wd
= fbuf
[ba
] & 0377; /* select char */
362 else wd
= (fbuf
[ba
] >> 8) & 0377;
363 fhd_csum
= fhd_csword (fhd_csum
, wd
); /* put in csum */
364 *ch
= wd
; /* return */
368 /* Write character to disk */
370 t_bool
fhd_putc (UNIT
*uptr
, uint32 ch
)
372 uint32 sf
= CW1_GETSF (fhd_cw1
); /* surface */
373 uint32 tk
= CW1_GETTK (fhd_cw1
); /* track */
374 uint32 ca
= CW2_GETCA (fhd_cw2
); /* char addr */
375 uint32 wa
= ca
>> 1; /* word addr */
376 uint32 ba
= (((sf
* FH_NUMTK
) + tk
) * FH_NUMWD
) + wa
; /* buffer offset */
377 uint16
*fbuf
= uptr
->filebuf
; /* buffer base */
379 ch
= ch
& 0377; /* mask char */
380 if (fhd_bad_wa (wa
)) return TRUE
; /* addr bad? */
381 fhd_cw2
= fhd_cw2
+ 1; /* incr char addr */
382 if (ca
& 1) fbuf
[ba
] = (fbuf
[ba
] & ~0377) | ch
; /* odd? low char */
383 else fbuf
[ba
] = (fbuf
[ba
] & 0377) | (ch
<< 8); /* even, hi char */
384 fhd_csum
= fhd_csword (fhd_csum
, ch
); /* put in csum */
385 if (ba
>= uptr
->hwmark
) uptr
->hwmark
= ba
+ 1; /* update hwmark */
389 /* Check word address */
391 t_bool
fhd_bad_wa (uint32 wa
)
393 if (wa
>= FH_NUMWD
) { /* bad address? */
394 fhd_ace
= 1; /* access error */
395 fhd_busy
= 0; /* abort operation */
402 /* Add character to checksum (parity) */
404 uint32
fhd_csword (uint32 cs
, uint32 ch
)
406 while (ch
) { /* count bits */
407 ch
= ch
& ~(ch
& (-(int32
) ch
));
408 cs
= cs
^ 0200; /* invert cs for each 1 */
415 t_stat
fhd_reset (DEVICE
*dptr
)
417 fhd_busy
= 0; /* reset state */
423 fhd_cw1
= fhd_cw2
= fhd_buf
= 0;
424 CLR_INT (INT_FHD
); /* clear int, enb */
426 sim_cancel (&fhd_unit
); /* cancel operation */
432 t_stat
fhd_attach (UNIT
*uptr
, char *cptr
)
435 uint32 ds_bytes
= FH_WDPSF
* sizeof (int16
);
437 if ((uptr
->flags
& UNIT_AUTO
) && (sz
= sim_fsize_name (cptr
))) {
438 sf
= (sz
+ ds_bytes
- 1) / ds_bytes
;
439 if (sf
>= FH_NUMSF
) sf
= FH_NUMSF
- 1;
440 uptr
->flags
= (uptr
->flags
& ~UNIT_SF
) |
443 uptr
->capac
= UNIT_GETSF (uptr
->flags
) * FH_WDPSF
;
444 return attach_unit (uptr
, cptr
);
447 /* Set size routine */
449 t_stat
fhd_set_size (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
)
451 if (val
< 0) return SCPE_IERR
;
452 if (uptr
->flags
& UNIT_ATT
) return SCPE_ALATT
;
453 uptr
->capac
= UNIT_GETSF (val
) * FH_WDPSF
;