First Commit of my working state
[simh.git] / NOVA / nova_dsk.c
CommitLineData
196ba1fc
PH
1/* nova_dsk.c: 4019 fixed head disk simulator\r
2\r
3 Copyright (c) 1993-2008, 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 dsk fixed head disk\r
27\r
28 04-Jul-07 BKR device name changed to DG's DSK from DEC's DK,\r
29 DEV_xxx macros now used for consistency,\r
30 added secret DG DIC function,\r
31 fixed boot info table size calculation\r
32 15-May-06 RMS Fixed bug in autosize attach (reported by David Gesswein)\r
33 04-Jan-04 RMS Changed sim_fsize calling sequence\r
34 26-Jul-03 RMS Fixed bug in set size routine\r
35 14-Mar-03 RMS Fixed variable capacity interaction with save/restore\r
36 03-Mar-03 RMS Fixed variable capacity and autosizing\r
37 03-Oct-02 RMS Added DIB\r
38 06-Jan-02 RMS Revised enable/disable support\r
39 23-Aug-01 RMS Fixed bug in write watermarking\r
40 26-Apr-01 RMS Added device enable/disable support\r
41 10-Dec-00 RMS Added Eclipse support\r
42 15-Oct-00 RMS Editorial changes\r
43 14-Apr-99 RMS Changed t_addr to unsigned\r
44\r
45 The 4019 is a head-per-track disk. To minimize overhead, the entire disk\r
46 is buffered in memory.\r
47*/\r
48\r
49#include "nova_defs.h"\r
50#include <math.h>\r
51\r
52#define UNIT_V_AUTO (UNIT_V_UF + 0) /* autosize */\r
53#define UNIT_V_PLAT (UNIT_V_UF + 1) /* #platters - 1 */\r
54#define UNIT_M_PLAT 07\r
55#define UNIT_PLAT (UNIT_M_PLAT << UNIT_V_PLAT)\r
56#define UNIT_GETP(x) ((((x) >> UNIT_V_PLAT) & UNIT_M_PLAT) + 1)\r
57#define UNIT_AUTO (1 << UNIT_V_AUTO)\r
58#define UNIT_PLAT (UNIT_M_PLAT << UNIT_V_PLAT)\r
59\r
60/* Constants */\r
61\r
62#define DSK_NUMWD 256 /* words/sector */\r
63#define DSK_NUMSC 8 /* sectors/track */\r
64#define DSK_NUMTR 128 /* tracks/disk */\r
65#define DSK_DKSIZE (DSK_NUMTR*DSK_NUMSC*DSK_NUMWD) /* words/disk */\r
66#define DSK_AMASK ((DSK_NUMDK*DSK_NUMTR*DSK_NUMSC) - 1)\r
67 /* address mask */\r
68#define DSK_NUMDK 8 /* disks/controller */\r
69#define GET_DISK(x) (((x) / (DSK_NUMSC * DSK_NUMTR)) & (DSK_NUMDK - 1))\r
70\r
71/* Parameters in the unit descriptor */\r
72\r
73#define FUNC u4 /* function */\r
74\r
75/* Status register */\r
76\r
77#define DSKS_WLS 020 /* write lock status */\r
78#define DSKS_DLT 010 /* data late error */\r
79#define DSKS_NSD 004 /* non-existent disk */\r
80#define DSKS_CRC 002 /* parity error */\r
81#define DSKS_ERR 001 /* error summary */\r
82#define DSKS_ALLERR (DSKS_WLS | DSKS_DLT | DSKS_NSD | DSKS_CRC | DSKS_ERR)\r
83\r
84/* Map logical sector numbers to physical sector numbers\r
85 (indexed by track<2:0>'sector)\r
86*/\r
87\r
88static const int32 sector_map[] = {\r
89 0, 2, 4, 6, 1, 3, 5, 7, 1, 3, 5, 7, 2, 4, 6, 0,\r
90 2, 4, 6, 0, 3, 5, 7, 1, 3, 5, 7, 1, 4, 6, 0, 2,\r
91 4, 6, 0, 2, 5, 7, 1, 3, 5, 7, 1, 3, 6, 0, 2, 4,\r
92 6, 0, 2, 4, 7, 1, 3, 5, 7, 1, 3, 5, 0, 2, 4, 6\r
93 };\r
94\r
95#define DSK_MMASK 077\r
96#define GET_SECTOR(x) ((int) fmod (sim_gtime() / ((double) (x)), \\r
97 ((double) DSK_NUMSC)))\r
98\r
99extern uint16 M[];\r
100extern UNIT cpu_unit;\r
101extern int32 int_req, dev_busy, dev_done, dev_disable;\r
102extern int32 saved_PC, SR, AMASK;\r
103\r
104int32 dsk_stat = 0; /* status register */\r
105int32 dsk_da = 0; /* disk address */\r
106int32 dsk_ma = 0; /* memory address */\r
107int32 dsk_wlk = 0; /* wrt lock switches */\r
108int32 dsk_stopioe = 0; /* stop on error */\r
109int32 dsk_time = 100; /* time per sector */\r
110\r
111DEVICE dsk_dev;\r
112int32 dsk (int32 pulse, int32 code, int32 AC);\r
113t_stat dsk_svc (UNIT *uptr);\r
114t_stat dsk_reset (DEVICE *dptr);\r
115t_stat dsk_boot (int32 unitno, DEVICE *dptr);\r
116t_stat dsk_attach (UNIT *uptr, char *cptr);\r
117t_stat dsk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);\r
118\r
119/* DSK data structures\r
120\r
121 dsk_dev device descriptor\r
122 dsk_unit unit descriptor\r
123 dsk_reg register list\r
124*/\r
125\r
126DIB dsk_dib = { DEV_DSK, INT_DSK, PI_DSK, &dsk };\r
127\r
128UNIT dsk_unit = {\r
129 UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF,\r
130 DSK_DKSIZE)\r
131 };\r
132\r
133REG dsk_reg[] = {\r
134 { ORDATA (STAT, dsk_stat, 16) },\r
135 { ORDATA (DA, dsk_da, 16) },\r
136 { ORDATA (MA, dsk_ma, 16) },\r
137 { FLDATA (BUSY, dev_busy, INT_V_DSK) },\r
138 { FLDATA (DONE, dev_done, INT_V_DSK) },\r
139 { FLDATA (DISABLE, dev_disable, INT_V_DSK) },\r
140 { FLDATA (INT, int_req, INT_V_DSK) },\r
141 { ORDATA (WLK, dsk_wlk, 8) },\r
142 { DRDATA (TIME, dsk_time, 24), REG_NZ + PV_LEFT },\r
143 { FLDATA (STOP_IOE, dsk_stopioe, 0) },\r
144 { NULL }\r
145 };\r
146\r
147MTAB dsk_mod[] = {\r
148 { UNIT_PLAT, (0 << UNIT_V_PLAT), NULL, "1P", &dsk_set_size },\r
149 { UNIT_PLAT, (1 << UNIT_V_PLAT), NULL, "2P", &dsk_set_size },\r
150 { UNIT_PLAT, (2 << UNIT_V_PLAT), NULL, "3P", &dsk_set_size },\r
151 { UNIT_PLAT, (3 << UNIT_V_PLAT), NULL, "4P", &dsk_set_size },\r
152 { UNIT_PLAT, (4 << UNIT_V_PLAT), NULL, "5P", &dsk_set_size },\r
153 { UNIT_PLAT, (5 << UNIT_V_PLAT), NULL, "6P", &dsk_set_size },\r
154 { UNIT_PLAT, (6 << UNIT_V_PLAT), NULL, "7P", &dsk_set_size },\r
155 { UNIT_PLAT, (7 << UNIT_V_PLAT), NULL, "8P", &dsk_set_size },\r
156 { UNIT_AUTO, UNIT_AUTO, "autosize", "AUTOSIZE", NULL },\r
157 { 0 }\r
158 };\r
159\r
160DEVICE dsk_dev = {\r
161 "DSK", &dsk_unit, dsk_reg, dsk_mod,\r
162 1, 8, 21, 1, 8, 16,\r
163 NULL, NULL, &dsk_reset,\r
164 &dsk_boot, &dsk_attach, NULL,\r
165 &dsk_dib, DEV_DISABLE\r
166 };\r
167\r
168\r
169/* IOT routine */\r
170\r
171int32 dsk (int32 pulse, int32 code, int32 AC)\r
172{\r
173int32 t, rval;\r
174\r
175rval = 0;\r
176switch (code) { /* decode IR<5:7> */\r
177\r
178 case ioDIA: /* DIA */\r
179 rval = dsk_stat & DSKS_ALLERR; /* read status */\r
180 break;\r
181\r
182 case ioDOA: /* DOA */\r
183 dsk_da = AC & DSK_AMASK; /* save disk addr */\r
184 break;\r
185\r
186 case ioDIB: /* DIB */\r
187 rval = dsk_ma & AMASK; /* read mem addr */\r
188 break;\r
189\r
190 case ioDOB: /* DOB */\r
191 dsk_ma = AC & AMASK; /* save mem addr */\r
192 break;\r
193\r
194 case ioDIC: /* DIC - undocumented DG feature(!) */\r
195 rval = 256 ; /* return fixed sector size for DG for now */\r
196 break ;\r
197 } /* end switch code */\r
198\r
199if (pulse) { /* any pulse? */\r
200 DEV_CLR_BUSY( INT_DSK ) ;\r
201 DEV_CLR_DONE( INT_DSK ) ;\r
202 DEV_UPDATE_INTR ;\r
203 dsk_stat = 0; /* clear status */\r
204 sim_cancel (&dsk_unit); /* stop I/O */\r
205 }\r
206\r
207if ((pulse == iopP) && ((dsk_wlk >> GET_DISK (dsk_da)) & 1)) { /* wrt lock? */\r
208 DEV_SET_DONE( INT_DSK ) ;\r
209 DEV_UPDATE_INTR ;\r
210 dsk_stat = DSKS_ERR + DSKS_WLS; /* set status */\r
211 return rval;\r
212 }\r
213\r
214if (pulse & 1) { /* read or write? */\r
215 if (((uint32) (dsk_da * DSK_NUMWD)) >= dsk_unit.capac) { /* inv sev? */\r
216 DEV_SET_DONE( INT_DSK ) ;\r
217 DEV_UPDATE_INTR ;\r
218 dsk_stat = DSKS_ERR + DSKS_NSD; /* set status */\r
219 return rval; /* done */\r
220 }\r
221 dsk_unit.FUNC = pulse; /* save command */\r
222 DEV_SET_BUSY( INT_DSK ) ;\r
223 DEV_UPDATE_INTR ;\r
224 t = sector_map[dsk_da & DSK_MMASK] - GET_SECTOR (dsk_time);\r
225 if (t < 0) t = t + DSK_NUMSC;\r
226 sim_activate (&dsk_unit, t * dsk_time); /* activate */\r
227 }\r
228return rval;\r
229}\r
230\r
231\r
232/* Unit service */\r
233\r
234t_stat dsk_svc (UNIT *uptr)\r
235{\r
236int32 i, da, pa;\r
237int16 *fbuf = uptr->filebuf;\r
238\r
239DEV_CLR_BUSY( INT_DSK ) ;\r
240DEV_SET_DONE( INT_DSK ) ;\r
241DEV_UPDATE_INTR ;\r
242\r
243if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */\r
244 dsk_stat = DSKS_ERR + DSKS_NSD; /* set status */\r
245 return IORETURN (dsk_stopioe, SCPE_UNATT);\r
246 }\r
247\r
248da = dsk_da * DSK_NUMWD; /* calc disk addr */\r
249if (uptr->FUNC == iopS) { /* read? */\r
250 for (i = 0; i < DSK_NUMWD; i++) { /* copy sector */\r
251 pa = MapAddr (0, (dsk_ma + i) & AMASK); /* map address */\r
252 if (MEM_ADDR_OK (pa)) M[pa] = fbuf[da + i];\r
253 }\r
254 dsk_ma = (dsk_ma + DSK_NUMWD) & AMASK;\r
255 }\r
256else if (uptr->FUNC == iopP) { /* write? */\r
257 for (i = 0; i < DSK_NUMWD; i++) { /* copy sector */\r
258 pa = MapAddr (0, (dsk_ma + i) & AMASK); /* map address */\r
259 fbuf[da + i] = M[pa];\r
260 }\r
261 if (((uint32) (da + i)) >= uptr->hwmark) /* past end? */\r
262 uptr->hwmark = da + i + 1; /* upd hwmark */\r
263 dsk_ma = (dsk_ma + DSK_NUMWD + 3) & AMASK;\r
264 }\r
265\r
266dsk_stat = 0; /* set status */\r
267return SCPE_OK;\r
268}\r
269\r
270\r
271/* Reset routine */\r
272\r
273t_stat dsk_reset (DEVICE *dptr)\r
274{\r
275dsk_stat = dsk_da = dsk_ma = 0;\r
276DEV_CLR_BUSY( INT_DSK ) ;\r
277DEV_CLR_DONE( INT_DSK ) ;\r
278DEV_UPDATE_INTR ;\r
279sim_cancel (&dsk_unit);\r
280return SCPE_OK;\r
281}\r
282\r
283\r
284/* Bootstrap routine */\r
285\r
286#define BOOT_START 0375\r
287#define BOOT_LEN (sizeof (boot_rom) / sizeof (int32))\r
288\r
289static const int32 boot_rom[] = {\r
290 0062677 /* IORST ; reset the I/O system */\r
291 , 0060120 /* NIOS DSK ; start the disk */\r
292 , 0000377 /* JMP 377 ; wait for the world */\r
293 } ;\r
294\r
295\r
296t_stat dsk_boot (int32 unitno, DEVICE *dptr)\r
297{\r
298int32 i;\r
299\r
300for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = (uint16) boot_rom[i];\r
301saved_PC = BOOT_START;\r
302SR = 0100000 + DEV_DSK;\r
303return SCPE_OK;\r
304}\r
305\r
306\r
307/* Attach routine */\r
308\r
309t_stat dsk_attach (UNIT *uptr, char *cptr)\r
310{\r
311uint32 sz, p;\r
312uint32 ds_bytes = DSK_DKSIZE * sizeof (int16);\r
313\r
314if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) {\r
315 p = (sz + ds_bytes - 1) / ds_bytes;\r
316 if (p >= DSK_NUMDK) p = DSK_NUMDK - 1;\r
317 uptr->flags = (uptr->flags & ~UNIT_PLAT) | (p << UNIT_V_PLAT);\r
318 }\r
319uptr->capac = UNIT_GETP (uptr->flags) * DSK_DKSIZE; /* set capacity */\r
320return attach_unit (uptr, cptr);\r
321}\r
322\r
323\r
324/* Change disk size */\r
325\r
326t_stat dsk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)\r
327{\r
328if (val < 0) return SCPE_IERR;\r
329if (uptr->flags & UNIT_ATT) return SCPE_ALATT;\r
330uptr->capac = UNIT_GETP (val) * DSK_DKSIZE;\r
331uptr->flags = uptr->flags & ~UNIT_AUTO;\r
332return SCPE_OK;\r
333}\r