1 /* sds_rad.c: SDS 940 fixed head disk simulator
2
3 Copyright (c) 2001-2008, 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
rad(uint32 fnc,uint32 inst,uint32 * dat)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) /* wrong chan? */
140 return SCPE_IERR;
141 if (CHC_GETCPW (inst) > 1) /* 1-2 char/word? */
142 return STOP_INVIOP;
143 if (sim_is_active (&rad_unit) || (alert == POT_RADA)) /* protocol viol? */
144 return STOP_INVIOP;
145 rad_err = 0; /* clr error */
146 rad_sba = 0; /* clr sec bptr */
147 chan_set_flag (rad_dib.chan, CHF_12B); /* 12B mode */
148 t = (rad_da & RAD_SCMASK) - GET_SECTOR (rad_time * RAD_NUMWD);
149 if (t <= 0) /* seek */
150 t = t + RAD_NUMSC;
151 sim_activate (&rad_unit, t * rad_time * (RAD_NUMWD / 2));
152 xfr_req = xfr_req & ~XFR_RAD; /* clr xfr flg */
153 break;
154
155 case IO_EOM1: /* EOM mode 1 */
156 new_ch = I_GETEOCH (inst); /* get new chan */
157 if (new_ch != rad_dib.chan) /* wrong chan? */
158 return SCPE_IERR;
159 if ((inst & 00600) == 00200) /* alert for sec */
160 alert = POT_RADS;
161 else if ((inst & 06600) == 0) { /* alert for addr */
162 if (sim_is_active (&rad_unit)) /* busy? */
163 rad_err = 1;
164 else {
165 rad_nobi = (inst & 01000)? 1: 0; /* save inc type */
166 alert = POT_RADA; /* set alert */
167 }
168 }
169 break;
170
171 case IO_DISC: /* disconnect */
172 rad_end_op (0); /* normal term */
173 if (inst & DEV_OUT) /* fill write */
174 return rad_fill (rad_sba);
175 break;
176
177 case IO_WREOR: /* write eor */
178 rad_end_op (CHF_EOR); /* eor term */
179 return rad_fill (rad_sba); /* fill write */
180
181 case IO_SKS: /* SKS */
182 new_ch = I_GETSKCH (inst); /* sks chan */
183 if (new_ch != rad_dib.chan) /* wrong chan? */
184 return SCPE_IERR;
185 t = I_GETSKCND (inst); /* sks cond */
186 lun = RAD_GETLUN (rad_da);
187 if (((t == 000) && !sim_is_active (&rad_unit)) || /* 10026: ready */
188 ((t == 004) && !rad_err) || /* 11026: !err */
189 ((t == 014) && !(rad_wrp & (1 << lun)))) /* 13026: !wrprot */
190 *dat = 1;
191 break;
192
193 case IO_READ: /* read */
194 p = (rad_da * RAD_NUMWD) + (rad_sba >> 1); /* buf wd addr */
195 xfr_req = xfr_req & ~XFR_RAD; /* clr xfr req */
196 if ((rad_unit.flags & UNIT_BUF) == 0) { /* not buffered? */
197 rad_end_op (CHF_ERR | CHF_EOR); /* set rad err */
198 CRETIOE (rad_stopioe, SCPE_UNATT);
199 }
200 if (p >= rad_unit.capac) { /* end of disk? */
201 rad_end_op (CHF_ERR | CHF_EOR); /* set rad err */
202 return SCPE_OK;
203 }
204 if (rad_sba & 1) /* odd byte? */
205 *dat = fbuf[p] & 07777;
206 else *dat = (fbuf[p] >> 12) & 07777; /* even */
207 rad_sba = rad_adjda (rad_sba, 1); /* next byte */
208 break;
209
210 case IO_WRITE:
211 p = (rad_da * RAD_NUMWD) + (rad_sba >> 1);
212 xfr_req = xfr_req & ~XFR_RAD; /* clr xfr req */
213 if ((rad_unit.flags & UNIT_BUF) == 0) { /* not buffered? */
214 rad_end_op (CHF_ERR | CHF_EOR); /* set rad err */
215 CRETIOE (rad_stopioe, SCPE_UNATT);
216 }
217 if ((p >= rad_unit.capac) || /* end of disk? */
218 (rad_wrp & (1 << RAD_GETLUN (rad_da)))) { /* write prot? */
219 rad_end_op (CHF_ERR | CHF_EOR); /* set rad err */
220 return SCPE_OK;
221 }
222 if (rad_sba & 1) /* odd byte? */
223 fbuf[p] = fbuf[p] | (*dat & 07777);
224 else fbuf[p] = (*dat & 07777) << 12; /* even */
225 if (p >= rad_unit.hwmark) /* mark hiwater */
226 rad_unit.hwmark = p + 1;
227 rad_sba = rad_adjda (rad_sba, 1); /* next byte */
228 break;
229
230 default:
231 CRETINS;
232 }
233
234 return SCPE_OK;
235 }
236
237 /* PIN routine */
238
pin_rads(uint32 num,uint32 * dat)239 t_stat pin_rads (uint32 num, uint32 *dat)
240 {
241 *dat = GET_SECTOR (rad_time * RAD_NUMWD); /* ret curr sec */
242 return SCPE_OK;
243 }
244
245 /* POT routine */
246
pot_rada(uint32 num,uint32 * dat)247 t_stat pot_rada (uint32 num, uint32 *dat)
248 {
249 rad_da = (*dat) & RAD_AMASK; /* save dsk addr */
250 return SCPE_OK;
251 }
252
253 /* Unit service and read/write */
254
rad_svc(UNIT * uptr)255 t_stat rad_svc (UNIT *uptr)
256 {
257 xfr_req = xfr_req | XFR_RAD; /* set xfr req */
258 sim_activate (&rad_unit, rad_time); /* activate */
259 return SCPE_OK;
260 }
261
262 /* Fill incomplete sector */
263
rad_fill(int32 sba)264 t_stat rad_fill (int32 sba)
265 {
266 uint32 p = rad_da * RAD_NUMWD;
267 uint32 *fbuf = rad_unit.filebuf;
268 int32 wa = (sba + 1) >> 1; /* whole words */
269
270 if (sba && (p < rad_unit.capac)) { /* fill needed? */
271 for ( ; wa < RAD_NUMWD; wa++)
272 fbuf[p + wa] = 0;
273 if ((p + wa) >= rad_unit.hwmark)
274 rad_unit.hwmark = p + wa + 1;
275 rad_adjda (sba, RAD_NUMWD - 1); /* inc da */
276 }
277 return SCPE_OK;
278 }
279
280 /* Adjust disk address */
281
rad_adjda(int32 sba,int32 inc)282 int32 rad_adjda (int32 sba, int32 inc)
283 {
284 sba = sba + inc;
285 if (rad_sba >= (RAD_NUMWD * 2)) { /* next sector? */
286 if (rad_nobi) rad_da = (rad_da & ~RAD_SCMASK) + /* within band? */
287 ((rad_da + 1) & RAD_SCMASK);
288 else rad_da = (rad_da & ~RAD_TRSCMASK) + /* cross band */
289 ((rad_da + 1) & RAD_TRSCMASK);
290 sba = 0; /* start new sec */
291 }
292 return sba;
293 }
294
295 /* Terminate disk operation */
296
rad_end_op(int32 fl)297 void rad_end_op (int32 fl)
298 {
299 if (fl) /* set flags */
300 chan_set_flag (rad_dib.chan, fl);
301 xfr_req = xfr_req & ~XFR_RAD; /* clear xfr */
302 sim_cancel (&rad_unit); /* stop */
303 if (fl & CHF_ERR) { /* error? */
304 chan_disc (rad_dib.chan); /* disconnect */
305 rad_err = 1; /* set rad err */
306 }
307 return;
308 }
309
310 /* Reset routine */
311
rad_reset(DEVICE * dptr)312 t_stat rad_reset (DEVICE *dptr)
313 {
314 chan_disc (rad_dib.chan); /* disconnect */
315 rad_nobi = 0; /* clear state */
316 rad_da = 0;
317 rad_sba = 0;
318 xfr_req = xfr_req & ~XFR_RAD; /* clr xfr req */
319 sim_cancel (&rad_unit); /* deactivate */
320 return SCPE_OK;
321 }
322