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