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