1 /* sds_rad.c: SDS 940 fixed head disk simulator
3 Copyright (c) 2001-2005, Robert M. Supnik
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:
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
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.
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.
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.
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 */
50 #define GET_SECTOR(x) ((int) fmod (sim_gtime() / ((double) (x)), \
51 ((double) RAD_NUMSC)))
53 extern uint32 xfr_req
;
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 */
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
);
77 /* RAD data structures
79 rad_dev device descriptor
80 rad_unit unit descriptor
84 DIB rad_dib
= { CHAN_E
, DEV_RAD
, XFR_RAD
, rad_tplt
, &rad
};
87 UDATA (&rad_svc
, UNIT_FIX
+UNIT_ATTABLE
+UNIT_BUFABLE
+UNIT_MUSTBUF
,
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) },
105 { MTAB_XTD
|MTAB_VDV
, 0, "CHANNEL", "CHANNEL",
106 &set_chan
, &show_chan
, NULL
},
111 "RAD", &rad_unit
, rad_reg
, rad_mod
,
113 NULL
, NULL
, &rad_reset
,
115 &rad_dib
, DEV_DISABLE
118 /* Fixed head disk routine
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
129 t_stat
rad (uint32 fnc
, uint32 inst
, uint32
*dat
)
131 int32 t
, lun
, new_ch
;
133 uint32
*fbuf
= rad_unit
.filebuf
;
135 switch (fnc
) { /* case function */
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? */
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 */
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? */
159 rad_nobi
= (inst
& 01000)? 1: 0; /* save inc type */
160 alert
= POT_RADA
; /* set alert */
165 case IO_DISC
: /* disconnect */
166 rad_end_op (0); /* normal term */
167 if (inst
& DEV_OUT
) return rad_fill (rad_sba
); /* fill write */
170 case IO_WREOR
: /* write eor */
171 rad_end_op (CHF_EOR
); /* eor term */
172 return rad_fill (rad_sba
); /* fill write */
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 */
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
);
192 if (p
>= rad_unit
.capac
) { /* end of disk? */
193 rad_end_op (CHF_ERR
| CHF_EOR
); /* set rad err */
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 */
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
);
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 */
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 */
228 t_stat
pin_rads (uint32 num
, uint32
*dat
)
230 *dat
= GET_SECTOR (rad_time
* RAD_NUMWD
); /* ret curr sec */
236 t_stat
pot_rada (uint32 num
, uint32
*dat
)
238 rad_da
= (*dat
) & RAD_AMASK
; /* save dsk addr */
242 /* Unit service and read/write */
244 t_stat
rad_svc (UNIT
*uptr
)
246 xfr_req
= xfr_req
| XFR_RAD
; /* set xfr req */
247 sim_activate (&rad_unit
, rad_time
); /* activate */
251 /* Fill incomplete sector */
253 t_stat
rad_fill (int32 sba
)
255 uint32 p
= rad_da
* RAD_NUMWD
;
256 uint32
*fbuf
= rad_unit
.filebuf
;
257 int32 wa
= (sba
+ 1) >> 1; /* whole words */
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 */
267 /* Adjust disk address */
269 int32
rad_adjda (int32 sba
, int32 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 */
282 /* Terminate disk operation */
284 void rad_end_op (int32 fl
)
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 */
298 t_stat
rad_reset (DEVICE
*dptr
)
300 chan_disc (rad_dib
.chan
); /* disconnect */
301 rad_nobi
= 0; /* clear state */
304 xfr_req
= xfr_req
& ~XFR_RAD
; /* clr xfr req */
305 sim_cancel (&rad_unit
); /* deactivate */