Commit | Line | Data |
---|---|---|
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 | |
53 | extern uint32 xfr_req;\r | |
54 | extern uint32 alert;\r | |
55 | extern int32 stop_invins, stop_invdev, stop_inviop;\r | |
56 | int32 rad_err = 0; /* error */\r | |
57 | int32 rad_nobi = 0; /* !incr x track */\r | |
58 | int32 rad_da = 0; /* disk address */\r | |
59 | int32 rad_sba = 0; /* sec byte addr */\r | |
60 | int32 rad_wrp = 0; /* write prot */\r | |
61 | int32 rad_time = 2; /* time per 12b */\r | |
62 | int32 rad_stopioe = 1; /* stop on error */\r | |
63 | DSPT rad_tplt[] = { /* template */\r | |
64 | { 1, 0 },\r | |
65 | { 1, DEV_OUT },\r | |
66 | { 0, 0 }\r | |
67 | };\r | |
68 | \r | |
69 | DEVICE rad_dev;\r | |
70 | t_stat rad_svc (UNIT *uptr);\r | |
71 | t_stat rad_reset (DEVICE *dptr);\r | |
72 | t_stat rad_fill (int32 sba);\r | |
73 | void rad_end_op (int32 fl);\r | |
74 | int32 rad_adjda (int32 sba, int32 inc);\r | |
75 | t_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 | |
84 | DIB rad_dib = { CHAN_E, DEV_RAD, XFR_RAD, rad_tplt, &rad };\r | |
85 | \r | |
86 | UNIT rad_unit = {\r | |
87 | UDATA (&rad_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF,\r | |
88 | RAD_SIZE)\r | |
89 | };\r | |
90 | \r | |
91 | REG 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 | |
104 | MTAB 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 | |
110 | DEVICE 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 | |
129 | t_stat rad (uint32 fnc, uint32 inst, uint32 *dat)\r | |
130 | {\r | |
131 | int32 t, lun, new_ch;\r | |
132 | uint32 p;\r | |
133 | uint32 *fbuf = rad_unit.filebuf;\r | |
134 | \r | |
135 | switch (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 | |
223 | return SCPE_OK;\r | |
224 | }\r | |
225 | \r | |
226 | /* PIN routine */\r | |
227 | \r | |
228 | t_stat pin_rads (uint32 num, uint32 *dat)\r | |
229 | {\r | |
230 | *dat = GET_SECTOR (rad_time * RAD_NUMWD); /* ret curr sec */\r | |
231 | return SCPE_OK;\r | |
232 | }\r | |
233 | \r | |
234 | /* POT routine */\r | |
235 | \r | |
236 | t_stat pot_rada (uint32 num, uint32 *dat)\r | |
237 | {\r | |
238 | rad_da = (*dat) & RAD_AMASK; /* save dsk addr */\r | |
239 | return SCPE_OK;\r | |
240 | }\r | |
241 | \r | |
242 | /* Unit service and read/write */\r | |
243 | \r | |
244 | t_stat rad_svc (UNIT *uptr)\r | |
245 | {\r | |
246 | xfr_req = xfr_req | XFR_RAD; /* set xfr req */\r | |
247 | sim_activate (&rad_unit, rad_time); /* activate */\r | |
248 | return SCPE_OK;\r | |
249 | }\r | |
250 | \r | |
251 | /* Fill incomplete sector */\r | |
252 | \r | |
253 | t_stat rad_fill (int32 sba)\r | |
254 | {\r | |
255 | uint32 p = rad_da * RAD_NUMWD;\r | |
256 | uint32 *fbuf = rad_unit.filebuf;\r | |
257 | int32 wa = (sba + 1) >> 1; /* whole words */\r | |
258 | \r | |
259 | if (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 | |
264 | return SCPE_OK;\r | |
265 | }\r | |
266 | \r | |
267 | /* Adjust disk address */\r | |
268 | \r | |
269 | int32 rad_adjda (int32 sba, int32 inc)\r | |
270 | {\r | |
271 | sba = sba + inc;\r | |
272 | if (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 | |
279 | return sba;\r | |
280 | }\r | |
281 | \r | |
282 | /* Terminate disk operation */\r | |
283 | \r | |
284 | void rad_end_op (int32 fl)\r | |
285 | {\r | |
286 | if (fl) chan_set_flag (rad_dib.chan, fl); /* set flags */\r | |
287 | xfr_req = xfr_req & ~XFR_RAD; /* clear xfr */\r | |
288 | sim_cancel (&rad_unit); /* stop */\r | |
289 | if (fl & CHF_ERR) { /* error? */\r | |
290 | chan_disc (rad_dib.chan); /* disconnect */\r | |
291 | rad_err = 1; /* set rad err */\r | |
292 | }\r | |
293 | return;\r | |
294 | }\r | |
295 | \r | |
296 | /* Reset routine */\r | |
297 | \r | |
298 | t_stat rad_reset (DEVICE *dptr)\r | |
299 | {\r | |
300 | chan_disc (rad_dib.chan); /* disconnect */\r | |
301 | rad_nobi = 0; /* clear state */\r | |
302 | rad_da = 0;\r | |
303 | rad_sba = 0;\r | |
304 | xfr_req = xfr_req & ~XFR_RAD; /* clr xfr req */\r | |
305 | sim_cancel (&rad_unit); /* deactivate */\r | |
306 | return SCPE_OK;\r | |
307 | }\r |