First Commit of my working state
[simh.git] / SDS / sds_rad.c
CommitLineData
196ba1fc
PH
1/* sds_rad.c: SDS 940 fixed head disk simulator\r
2\r
3 Copyright (c) 2001-2005, 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 rad fixed head disk\r
27\r
28 The fixed head disk is a head-per-track disk, with up to four disks. Each\r
29 disk is divided into two logical units. Reads and writes cannot cross logical\r
30 unit boundaries. The fixed head disk transfers 12b characters, rather than 6b\r
31 characters. To minimize overhead, the disk is buffered in memory.\r
32*/\r
33\r
34#include "sds_defs.h"\r
35#include <math.h>\r
36\r
37/* Constants */\r
38\r
39#define RAD_NUMWD 64 /* words/sector */\r
40#define RAD_NUMSC 64 /* sectors/track */\r
41#define RAD_NUMTR 64 /* tracks/log unit */\r
42#define RAD_NUMLU 8 /* log units/ctrl */\r
43#define RAD_SCSIZE (RAD_NUMLU*RAD_NUMTR*RAD_NUMSC) /* sectors/disk */\r
44#define RAD_AMASK (RAD_SCSIZE - 1) /* sec addr mask */\r
45#define RAD_SIZE (RAD_SCSIZE * RAD_NUMWD) /* words/disk */\r
46#define RAD_GETLUN(x) ((x) / (RAD_NUMTR * RAD_NUMSC))\r
47#define RAD_SCMASK (RAD_NUMSC - 1) /* sector mask */\r
48#define RAD_TRSCMASK ((RAD_NUMSC * RAD_NUMTR) - 1) /* track/sec mask */\r
49\r
50#define GET_SECTOR(x) ((int) fmod (sim_gtime() / ((double) (x)), \\r
51 ((double) RAD_NUMSC)))\r
52\r
53extern uint32 xfr_req;\r
54extern uint32 alert;\r
55extern int32 stop_invins, stop_invdev, stop_inviop;\r
56int32 rad_err = 0; /* error */\r
57int32 rad_nobi = 0; /* !incr x track */\r
58int32 rad_da = 0; /* disk address */\r
59int32 rad_sba = 0; /* sec byte addr */\r
60int32 rad_wrp = 0; /* write prot */\r
61int32 rad_time = 2; /* time per 12b */\r
62int32 rad_stopioe = 1; /* stop on error */\r
63DSPT rad_tplt[] = { /* template */\r
64 { 1, 0 },\r
65 { 1, DEV_OUT },\r
66 { 0, 0 }\r
67 };\r
68\r
69DEVICE rad_dev;\r
70t_stat rad_svc (UNIT *uptr);\r
71t_stat rad_reset (DEVICE *dptr);\r
72t_stat rad_fill (int32 sba);\r
73void rad_end_op (int32 fl);\r
74int32 rad_adjda (int32 sba, int32 inc);\r
75t_stat rad (uint32 fnc, uint32 inst, uint32 *dat);\r
76\r
77/* RAD data structures\r
78\r
79 rad_dev device descriptor\r
80 rad_unit unit descriptor\r
81 rad_reg register list\r
82*/\r
83\r
84DIB rad_dib = { CHAN_E, DEV_RAD, XFR_RAD, rad_tplt, &rad };\r
85\r
86UNIT rad_unit = {\r
87 UDATA (&rad_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF,\r
88 RAD_SIZE)\r
89 };\r
90\r
91REG rad_reg[] = {\r
92 { ORDATA (DA, rad_da, 15) },\r
93 { GRDATA (SA, rad_sba, 8, 6, 1) },\r
94 { FLDATA (BP, rad_sba, 0) },\r
95 { FLDATA (XFR, xfr_req, XFR_V_RAD) },\r
96 { FLDATA (NOBD, rad_nobi, 0) },\r
97 { FLDATA (ERR, rad_err, 0) },\r
98 { ORDATA (PROT, rad_wrp, 8) },\r
99 { DRDATA (TIME, rad_time, 24), REG_NZ + PV_LEFT },\r
100 { FLDATA (STOP_IOE, rad_stopioe, 0) },\r
101 { NULL }\r
102 };\r
103\r
104MTAB rad_mod[] = {\r
105 { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL",\r
106 &set_chan, &show_chan, NULL },\r
107 { 0 }\r
108 };\r
109\r
110DEVICE rad_dev = {\r
111 "RAD", &rad_unit, rad_reg, rad_mod,\r
112 1, 8, 21, 1, 8, 24,\r
113 NULL, NULL, &rad_reset,\r
114 NULL, NULL, NULL,\r
115 &rad_dib, DEV_DISABLE\r
116 };\r
117\r
118/* Fixed head disk routine\r
119 \r
120 conn - inst = EOM0, dat = NULL\r
121 eom1 - inst = EOM1, dat = NULL\r
122 sks - inst = SKS, dat = ptr to result\r
123 disc - inst = device number, dat = NULL\r
124 wreor - inst = device number, dat = NULL\r
125 read - inst = device number, dat = ptr to data\r
126 write - inst = device number, dat = ptr to result\r
127*/\r
128\r
129t_stat rad (uint32 fnc, uint32 inst, uint32 *dat)\r
130{\r
131int32 t, lun, new_ch;\r
132uint32 p;\r
133uint32 *fbuf = rad_unit.filebuf;\r
134\r
135switch (fnc) { /* case function */\r
136\r
137 case IO_CONN: /* connect */\r
138 new_ch = I_GETEOCH (inst); /* get new chan */\r
139 if (new_ch != rad_dib.chan) return SCPE_IERR; /* wrong chan? */\r
140 if (CHC_GETCPW (inst) > 1) return STOP_INVIOP; /* 1-2 char/word? */\r
141 if (sim_is_active (&rad_unit) || (alert == POT_RADA)) /* protocol viol? */\r
142 return STOP_INVIOP;\r
143 rad_err = 0; /* clr error */\r
144 rad_sba = 0; /* clr sec bptr */\r
145 chan_set_flag (rad_dib.chan, CHF_12B); /* 12B mode */\r
146 t = (rad_da & RAD_SCMASK) - GET_SECTOR (rad_time * RAD_NUMWD);\r
147 if (t <= 0) t = t + RAD_NUMSC; /* seek */\r
148 sim_activate (&rad_unit, t * rad_time * (RAD_NUMWD / 2));\r
149 xfr_req = xfr_req & ~XFR_RAD; /* clr xfr flg */\r
150 break;\r
151\r
152 case IO_EOM1: /* EOM mode 1 */\r
153 new_ch = I_GETEOCH (inst); /* get new chan */\r
154 if (new_ch != rad_dib.chan) return SCPE_IERR; /* wrong chan? */\r
155 if ((inst & 00600) == 00200) alert = POT_RADS; /* alert for sec */ \r
156 else if ((inst & 06600) == 0) { /* alert for addr */\r
157 if (sim_is_active (&rad_unit)) rad_err = 1; /* busy? */\r
158 else {\r
159 rad_nobi = (inst & 01000)? 1: 0; /* save inc type */\r
160 alert = POT_RADA; /* set alert */\r
161 }\r
162 }\r
163 break;\r
164\r
165 case IO_DISC: /* disconnect */\r
166 rad_end_op (0); /* normal term */\r
167 if (inst & DEV_OUT) return rad_fill (rad_sba); /* fill write */\r
168 break;\r
169\r
170 case IO_WREOR: /* write eor */\r
171 rad_end_op (CHF_EOR); /* eor term */\r
172 return rad_fill (rad_sba); /* fill write */\r
173\r
174 case IO_SKS: /* SKS */\r
175 new_ch = I_GETSKCH (inst); /* sks chan */\r
176 if (new_ch != rad_dib.chan) return SCPE_IERR; /* wrong chan? */\r
177 t = I_GETSKCND (inst); /* sks cond */\r
178 lun = RAD_GETLUN (rad_da);\r
179 if (((t == 000) && !sim_is_active (&rad_unit)) || /* 10026: ready */\r
180 ((t == 004) && !rad_err) || /* 11026: !err */\r
181 ((t == 014) && !(rad_wrp & (1 << lun)))) /* 13026: !wrprot */\r
182 *dat = 1;\r
183 break;\r
184\r
185 case IO_READ: /* read */\r
186 p = (rad_da * RAD_NUMWD) + (rad_sba >> 1); /* buf wd addr */\r
187 xfr_req = xfr_req & ~XFR_RAD; /* clr xfr req */\r
188 if ((rad_unit.flags & UNIT_BUF) == 0) { /* not buffered? */\r
189 rad_end_op (CHF_ERR | CHF_EOR); /* set rad err */\r
190 CRETIOE (rad_stopioe, SCPE_UNATT);\r
191 }\r
192 if (p >= rad_unit.capac) { /* end of disk? */\r
193 rad_end_op (CHF_ERR | CHF_EOR); /* set rad err */\r
194 return SCPE_OK;\r
195 }\r
196 if (rad_sba & 1) *dat = fbuf[p] & 07777; /* odd byte? */\r
197 else *dat = (fbuf[p] >> 12) & 07777; /* even */\r
198 rad_sba = rad_adjda (rad_sba, 1); /* next byte */\r
199 break;\r
200\r
201 case IO_WRITE:\r
202 p = (rad_da * RAD_NUMWD) + (rad_sba >> 1);\r
203 xfr_req = xfr_req & ~XFR_RAD; /* clr xfr req */\r
204 if ((rad_unit.flags & UNIT_BUF) == 0) { /* not buffered? */\r
205 rad_end_op (CHF_ERR | CHF_EOR); /* set rad err */\r
206 CRETIOE (rad_stopioe, SCPE_UNATT);\r
207 }\r
208 if ((p >= rad_unit.capac) || /* end of disk? */\r
209 (rad_wrp & (1 << RAD_GETLUN (rad_da)))) { /* write prot? */\r
210 rad_end_op (CHF_ERR | CHF_EOR); /* set rad err */\r
211 return SCPE_OK;\r
212 }\r
213 if (rad_sba & 1) fbuf[p] = fbuf[p] | (*dat & 07777); /* odd byte? */\r
214 else fbuf[p] = (*dat & 07777) << 12; /* even */\r
215 if (p >= rad_unit.hwmark) rad_unit.hwmark = p + 1; /* mark hiwater */\r
216 rad_sba = rad_adjda (rad_sba, 1); /* next byte */\r
217 break;\r
218\r
219 default:\r
220 CRETINS;\r
221 }\r
222\r
223return SCPE_OK;\r
224}\r
225\r
226/* PIN routine */\r
227\r
228t_stat pin_rads (uint32 num, uint32 *dat)\r
229{\r
230*dat = GET_SECTOR (rad_time * RAD_NUMWD); /* ret curr sec */\r
231return SCPE_OK;\r
232}\r
233\r
234/* POT routine */\r
235\r
236t_stat pot_rada (uint32 num, uint32 *dat)\r
237{\r
238rad_da = (*dat) & RAD_AMASK; /* save dsk addr */\r
239return SCPE_OK;\r
240}\r
241\r
242/* Unit service and read/write */\r
243\r
244t_stat rad_svc (UNIT *uptr)\r
245{\r
246xfr_req = xfr_req | XFR_RAD; /* set xfr req */\r
247sim_activate (&rad_unit, rad_time); /* activate */\r
248return SCPE_OK;\r
249}\r
250\r
251/* Fill incomplete sector */\r
252\r
253t_stat rad_fill (int32 sba)\r
254{\r
255uint32 p = rad_da * RAD_NUMWD;\r
256uint32 *fbuf = rad_unit.filebuf;\r
257int32 wa = (sba + 1) >> 1; /* whole words */\r
258\r
259if (sba && (p < rad_unit.capac)) { /* fill needed? */\r
260 for ( ; wa < RAD_NUMWD; wa++) fbuf[p + wa] = 0;\r
261 if ((p + wa) >= rad_unit.hwmark) rad_unit.hwmark = p + wa + 1;\r
262 rad_adjda (sba, RAD_NUMWD - 1); /* inc da */\r
263 }\r
264return SCPE_OK;\r
265}\r
266\r
267/* Adjust disk address */\r
268\r
269int32 rad_adjda (int32 sba, int32 inc)\r
270{\r
271sba = sba + inc;\r
272if (rad_sba >= (RAD_NUMWD * 2)) { /* next sector? */\r
273 if (rad_nobi) rad_da = (rad_da & ~RAD_SCMASK) + /* within band? */\r
274 ((rad_da + 1) & RAD_SCMASK);\r
275 else rad_da = (rad_da & ~RAD_TRSCMASK) + /* cross band */\r
276 ((rad_da + 1) & RAD_TRSCMASK);\r
277 sba = 0; /* start new sec */\r
278 }\r
279return sba;\r
280}\r
281\r
282/* Terminate disk operation */\r
283\r
284void rad_end_op (int32 fl)\r
285{\r
286if (fl) chan_set_flag (rad_dib.chan, fl); /* set flags */\r
287xfr_req = xfr_req & ~XFR_RAD; /* clear xfr */\r
288sim_cancel (&rad_unit); /* stop */\r
289if (fl & CHF_ERR) { /* error? */\r
290 chan_disc (rad_dib.chan); /* disconnect */\r
291 rad_err = 1; /* set rad err */\r
292 }\r
293return;\r
294}\r
295\r
296/* Reset routine */\r
297\r
298t_stat rad_reset (DEVICE *dptr)\r
299{\r
300chan_disc (rad_dib.chan); /* disconnect */\r
301rad_nobi = 0; /* clear state */\r
302rad_da = 0;\r
303rad_sba = 0;\r
304xfr_req = xfr_req & ~XFR_RAD; /* clr xfr req */\r
305sim_cancel (&rad_unit); /* deactivate */\r
306return SCPE_OK;\r
307}\r