First Commit of my working state
[simh.git] / H316 / h316_fhd.c
1 /* h316_fhd.c: H316/516 fixed head simulator
2
3 Copyright (c) 2003-2006, 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 15-May-06 RMS Fixed bug in autosize attach (reported by David Gesswein)
29 04-Jan-04 RMS Changed sim_fsize calling sequence
30
31 These head-per-track devices are buffered in memory, to minimize overhead.
32 */
33
34 #include "h316_defs.h"
35 #include <math.h>
36
37 /* Constants */
38
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 */
45 #define UNIT_M_SF 017
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)
49
50 /* Command word 1 */
51
52 #define CW1_RW 0100000 /* read vs write */
53 #define CW1_V_SF 10 /* surface */
54 #define CW1_M_SF 017
55 #define CW1_GETSF(x) (((x) >> CW1_V_SF) & CW1_M_SF)
56 #define CW1_V_TK 4 /* track */
57 #define CW1_M_TK 077
58 #define CW1_GETTK(x) (((x) >> CW1_V_TK) & CW1_M_TK)
59
60 /* Command word 2 */
61
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)
65
66 #define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \
67 ((double) FH_NUMWD)))
68
69 /* OTA states */
70
71 #define OTA_NOP 0 /* normal */
72 #define OTA_CW1 1 /* expecting CW1 */
73 #define OTA_CW2 2 /* expecting CW2 */
74
75 extern int32 dev_int, dev_enb, chan_req;
76 extern int32 stop_inst;
77 extern uint32 dma_ad[DMA_MAX];
78
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 */
92
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);
105
106 /* FHD data structures
107
108 fhd_dev device descriptor
109 fhd_unit unit descriptor
110 fhd_mod unit modifiers
111 fhd_reg register list
112 */
113
114 DIB fhd_dib = { FHD, IOBUS, 1, &fhdio };
115
116 UNIT fhd_unit = {
117 UDATA (&fhd_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF,
118 FH_WDPSF)
119 };
120
121 REG fhd_reg[] = {
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) },
138 { NULL }
139 };
140
141 MTAB fhd_mod[] = {
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 },
167 { 0 }
168 };
169
170 DEVICE fhd_dev = {
171 "FHD", &fhd_unit, fhd_reg, fhd_mod,
172 1, 8, 22, 1, 8, 16,
173 NULL, NULL, &fhd_reset,
174 NULL, &fhd_attach, NULL,
175 &fhd_dib, DEV_DISABLE
176 };
177
178 /* IO routines */
179
180 int32 fhdio (int32 inst, int32 fnc, int32 dat, int32 dev)
181 {
182 switch (inst) { /* case on opcode */
183
184 case ioOCP: /* control */
185 if (fnc == 04) { /* terminate output? */
186 fhd_eor = 1; /* stop */
187 CLR_INT (INT_FHD); /* clear int req */
188 }
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);
192 break;
193
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 */
201 return IOSKIP (dat);
202 }
203 break;
204
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 */
210 }
211 break;
212
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 */
219 return IOSKIP (dat);
220 break;
221
222 case ioEND:
223 fhd_eor = 1;
224 break;
225 }
226
227 return dat;
228 }
229
230 /* Start new operation */
231
232 void fhd_go (uint32 dma)
233 {
234 int32 ch = fhd_dib.chan - 1; /* DMA/DMC chan */
235
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 */
241 fhd_ace = 0;
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 */
249 }
250 return;
251 }
252
253 /* Process command word 1 */
254
255 void fhd_go1 (uint32 dat)
256 {
257 int32 ch = fhd_dib.chan - 1; /* DMA/DMC chan */
258
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 */
263 return;
264 }
265
266 /* Process command word 2 - initiate seek */
267
268 void fhd_go2 (uint32 dat)
269 {
270 int32 ch = fhd_dib.chan - 1; /* DMA/DMC chan */
271 uint32 sf = CW1_GETSF (fhd_cw1); /* surface */
272 int32 t, wa;
273
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 */
282 SET_INT (INT_FHD);
283 return;
284 }
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 */
288 }
289 else {
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 */
293 }
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 */
297 return;
298 }
299
300 /* Unit service */
301
302 t_stat fhd_svc (UNIT *uptr)
303 {
304 int32 ch = fhd_dib.chan - 1; /* DMA/DMC chan (-1 if IO bus) */
305 uint32 c1, c2;
306
307 if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */
308 fhd_ace = 1; /* access error */
309 fhd_busy = 0; /* abort operation */
310 SET_INT (INT_FHD);
311 return IORETURN (fhd_stopioe, SCPE_UNATT);
312 }
313
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);
320 }
321 fhd_putc (uptr, fhd_csum); /* store csum */
322 }
323 else { /* read */
324 fhd_getc (uptr, &c1); /* get csum */
325 if (fhd_csum) fhd_dte = 1; /* if csum != 0, err */
326 }
327 fhd_busy = 0; /* operation complete */
328 SET_INT (INT_FHD);
329 return SCPE_OK;
330 }
331
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;
335 }
336 else {
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;
340 }
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 */
344 return SCPE_OK;
345 }
346
347 /* Read character from disk */
348
349 t_bool fhd_getc (UNIT *uptr, uint32 *ch)
350 {
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 */
357 uint32 wd;
358
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 */
365 return FALSE;
366 }
367
368 /* Write character to disk */
369
370 t_bool fhd_putc (UNIT *uptr, uint32 ch)
371 {
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 */
378
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 */
386 return FALSE;
387 }
388
389 /* Check word address */
390
391 t_bool fhd_bad_wa (uint32 wa)
392 {
393 if (wa >= FH_NUMWD) { /* bad address? */
394 fhd_ace = 1; /* access error */
395 fhd_busy = 0; /* abort operation */
396 SET_INT (INT_FHD);
397 return TRUE;
398 }
399 return FALSE;
400 }
401
402 /* Add character to checksum (parity) */
403
404 uint32 fhd_csword (uint32 cs, uint32 ch)
405 {
406 while (ch) { /* count bits */
407 ch = ch & ~(ch & (-(int32) ch));
408 cs = cs ^ 0200; /* invert cs for each 1 */
409 }
410 return cs;
411 }
412
413 /* Reset routine */
414
415 t_stat fhd_reset (DEVICE *dptr)
416 {
417 fhd_busy = 0; /* reset state */
418 fhd_rdy = 0;
419 fhd_ace = 0;
420 fhd_dte = 0;
421 fhd_eor = 0;
422 fhd_otas = OTA_NOP;
423 fhd_cw1 = fhd_cw2 = fhd_buf = 0;
424 CLR_INT (INT_FHD); /* clear int, enb */
425 CLR_ENB (INT_FHD);
426 sim_cancel (&fhd_unit); /* cancel operation */
427 return SCPE_OK;
428 }
429
430 /* Attach routine */
431
432 t_stat fhd_attach (UNIT *uptr, char *cptr)
433 {
434 uint32 sz, sf;
435 uint32 ds_bytes = FH_WDPSF * sizeof (int16);
436
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) |
441 (sf << UNIT_V_SF);
442 }
443 uptr->capac = UNIT_GETSF (uptr->flags) * FH_WDPSF;
444 return attach_unit (uptr, cptr);
445 }
446
447 /* Set size routine */
448
449 t_stat fhd_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)
450 {
451 if (val < 0) return SCPE_IERR;
452 if (uptr->flags & UNIT_ATT) return SCPE_ALATT;
453 uptr->capac = UNIT_GETSF (val) * FH_WDPSF;
454 return SCPE_OK;
455 }