First Commit of my working state
[simh.git] / SDS / sds_drm.c
1 /* sds_drm.c: SDS 940 Project Genie drum simulator
2
3 Copyright (c) 2002-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 drm drum
27
28 The drum is buffered in memory.
29
30 Note: the Project Genie documentation and the actual monitor sources disagree
31 on the I/O instruction definitions for the drum. The simulator follows the
32 monitor sources, as follows:
33
34 DCC OP 00230404B RESET DRUM CHANNEL
35 DSC OP 00230204B START DRUM CHANNEL (NO CHAIN)
36 DRA OP 00230504B READ DRUM TIMING COUNTER INTO 21B
37 DSR OP 04030204B SKIP IF DRUM NOT BUSY
38 DSE OP 04037404B SKIP IF NO DRUM ERROR
39 */
40
41 #include "sds_defs.h"
42 #include <math.h>
43
44 /* Constants */
45
46 #define DRM_N_WD 11 /* word addr width */
47 #define DRM_V_WD 0 /* position */
48 #define DRM_M_WD ((1 << DRM_N_WD) - 1) /* word mask */
49 #define DRM_NUMWD (1 << DRM_N_WD) /* words/sector */
50 #define DRM_NUMGP 236 /* gap/sector */
51 #define DRM_PHYWD (DRM_NUMWD + DRM_NUMGP) /* phys wds/sector */
52 #define DRM_N_SC 3 /* sect addr width */
53 #define DRM_V_SC (DRM_N_WD) /* position */
54 #define DRM_M_SC ((1 << DRM_N_SC) - 1) /* sector mask */
55 #define DRM_NUMSC (1 << DRM_N_SC) /* sectors/track */
56 #define DRM_N_TR 7 /* track addr width */
57 #define DRM_V_TR (DRM_N_WD+DRM_N_SC) /* position */
58 #define DRM_M_TR ((1 << DRM_N_TR) - 1) /* track mask */
59 #define DRM_NUMTR 84 /* tracks/drum */
60 #define DRM_N_ADDR (DRM_N_WD+DRM_N_SC+DRM_N_TR) /* drum addr width */
61 #define DRM_SWMASK ((1 << (DRM_N_WD+DRM_N_SC)) - 1)/* sector+word mask */
62 #define DRM_DAMASK ((1 << DRM_N_ADDR) - 1) /* drum addr mask */
63 #define DRM_SIZE (DRM_NUMTR*DRM_NUMSC*DRM_NUMWD) /* words/disk */
64 #define DRM_WCMASK 037777 /* wc mask */
65 #define DRM_GETSC(x) (((x) >> DRM_V_SC) & DRM_M_SC)
66
67 #define DRM_PC 020
68 #define DRM_AD 021
69 #define DRM_ADAT (1 << (DRM_N_WD + DRM_N_SC)) /* data flag */
70
71 #define DRM_SFET 0 /* fetch state */
72 #define DRM_SFCA 1 /* fetch CA */
73 #define DRM_SFDA 2 /* fetch DA */
74 #define DRM_SXFR 3 /* xfer */
75
76 #define DRM_V_OP 21 /* drum op */
77 #define DRM_M_OP 07
78 #define DRM_V_RW 20
79 #define DRM_GETOP(x) (((x) >> DRM_V_OP) & DRM_M_OP)
80 #define DRM_GETRW(x) (((x) >> DRM_V_RW) & 1)
81 #define DRM_OXF 0 /* xfer */
82 #define DRM_OCX 1 /* cond xfer */
83 #define DRM_OBR 2 /* branch */
84 #define DRM_ORS 3 /* reset error */
85 #define DRM_END 4 /* end prog */
86 #define DRM_EIE 5 /* end int if err */
87 #define DRM_EIU 7 /* end int uncond */
88
89 #define GET_TWORD(x) ((int32) fmod (sim_gtime() / ((double) (x)), \
90 ((double) (DRM_NUMSC * DRM_PHYWD))))
91
92 extern uint32 M[]; /* memory */
93 extern uint32 alert, int_req;
94 extern int32 stop_invins, stop_invdev, stop_inviop;
95 uint32 drm_da = 0; /* disk address */
96 uint32 drm_ca = 0; /* core address */
97 uint32 drm_wc = 0; /* word count */
98 int32 drm_par = 0; /* cumulative par */
99 int32 drm_err = 0; /* error */
100 int32 drm_rw = 0; /* read/write */
101 int32 drm_sta = 0; /* drum state */
102 int32 drm_ftime = 3; /* time to fetch */
103 int32 drm_xtime = 1; /* time to xfr */
104 int32 drm_stopioe = 1; /* stop on error */
105
106 DEVICE drm_dev;
107 t_stat drm (uint32 fnc, uint32 inst, uint32 *dat);
108 t_stat drm_svc (UNIT *uptr);
109 t_stat drm_reset (DEVICE *dptr);
110
111 /* DRM data structures
112
113 drm_dev device descriptor
114 drm_unit unit descriptor
115 drm_reg register list
116 */
117
118 DIB drm_dib = { -1, DEV3_GDRM, 0, NULL, &drm };
119
120 UNIT drm_unit = {
121 UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF,
122 DRM_SIZE)
123 };
124
125 REG drm_reg[] = {
126 { ORDATA (DA, drm_da, DRM_N_ADDR) },
127 { ORDATA (CA, drm_ca, 16) },
128 { ORDATA (WC, drm_wc, 14) },
129 { ORDATA (PAR, drm_par, 12) },
130 { FLDATA (RW, drm_rw, 0) },
131 { FLDATA (ERR, drm_err, 0) },
132 { ORDATA (STA, drm_sta, 2) },
133 { DRDATA (FTIME, drm_ftime, 24), REG_NZ + PV_LEFT },
134 { DRDATA (XTIME, drm_xtime, 24), REG_NZ + PV_LEFT },
135 { FLDATA (STOP_IOE, drm_stopioe, 0) },
136 { NULL }
137 };
138
139 DEVICE drm_dev = {
140 "DRM", &drm_unit, drm_reg, NULL,
141 1, 8, DRM_N_ADDR, 1, 8, 24,
142 NULL, NULL, &drm_reset,
143 NULL, NULL, NULL,
144 &drm_dib, DEV_DISABLE | DEV_DIS
145 };
146
147 /* Drum routine - EOM/SKS 3xx04 */
148
149 t_stat drm (uint32 fnc, uint32 inst, uint32 *dat)
150 {
151 int32 t, op = inst & 07700;
152
153 switch (fnc) {
154
155 case IO_CONN: /* connect */
156 if (op == 00400) return drm_reset (&drm_dev); /* EOM 404 = reset */
157 if (op == 00500) { /* EOM 504 = read DA */
158 if (sim_is_active (&drm_unit)) return SCPE_OK; /* must be idle */
159 t = GET_TWORD (drm_xtime); /* get position */
160 if (t < DRM_NUMGP) M[DRM_AD] = DRM_NUMWD - t; /* in gap? */
161 else M[DRM_AD] = (t - DRM_NUMGP) | DRM_ADAT;/* in data */
162 }
163 else if (op == 00200) { /* EOM 204 = start */
164 if (sim_is_active (&drm_unit)) return SCPE_OK; /* must be idle */
165 drm_sta = DRM_SFET; /* state = fetch */
166 sim_activate (&drm_unit, drm_ftime); /* activate */
167 }
168 else CRETINS;
169 break;
170
171 case IO_SKS: /* SKS */
172 if (((op == 07400) && !drm_err) || /* 37404: no err */
173 ((op == 00200) && !sim_is_active (&drm_unit))) /* 30204: idle */
174 *dat = 1;
175 break;
176
177 default:
178 return SCPE_IERR;
179 }
180
181 return SCPE_OK;
182 }
183
184 /* Unit service */
185
186 t_stat drm_svc (UNIT *uptr)
187 {
188 int32 t, rda;
189 uint32 dpc, dwd;
190 uint32 *fbuf = uptr->filebuf;
191
192 if (drm_sta != DRM_SXFR) { /* fetch drum prog? */
193 dpc = M[DRM_PC]; /* get drum PC */
194 dwd = M[dpc & PAMASK]; /* get drum inst */
195 M[DRM_PC] = (dpc + 1) & PAMASK; /* update drum PC */
196 if (drm_sta == DRM_SFCA) { /* fetch core addr? */
197 drm_rw = DRM_GETRW (dwd); /* set op */
198 drm_ca = dwd & PAMASK; /* set core addr */
199 drm_sta = DRM_SFDA; /* next is disk addr */
200 }
201 else if (drm_sta == DRM_SFDA) { /* fetch disk addr? */
202 drm_da = dwd & DRM_DAMASK; /* set disk addr */
203 drm_sta = DRM_SXFR; /* next is xfer */
204 drm_par = 0; /* init parity */
205 rda = (drm_da & DRM_SWMASK) + (DRM_GETSC (drm_da) * DRM_NUMGP);
206 t = rda - GET_TWORD (drm_xtime); /* difference */
207 if (t <= 0) t = t + (DRM_NUMSC * DRM_PHYWD); /* add trk lnt */
208 sim_activate (&drm_unit, t * drm_xtime); /* activate */
209 }
210 else {
211 switch (DRM_GETOP (dwd)) {
212
213 case DRM_OCX: /* cond xfr */
214 if (drm_err) { /* error? */
215 int_req = int_req | INT_DRM; /* req int */
216 return SCPE_OK; /* done */
217 }
218 case DRM_OXF: /* transfer */
219 drm_wc = dwd & DRM_WCMASK; /* save wc */
220 drm_sta = DRM_SFCA; /* next state */
221 break;
222
223 case DRM_OBR: /* branch */
224 M[DRM_PC] = dwd & PAMASK; /* new drum PC */
225 break;
226
227 case DRM_END: /* end */
228 return SCPE_OK;
229
230 case DRM_EIE: /* end, int if err */
231 if (!drm_err) return SCPE_OK;
232
233 case DRM_EIU: /* end, int uncond */
234 int_req = int_req | INT_DRM;
235 return SCPE_OK;
236 } /* end switch */
237 } /* end else sta */
238 sim_activate (uptr, drm_ftime); /* fetch next word */
239 } /* end if !xfr */
240 else { /* transfer word */
241 if ((uptr->flags & UNIT_BUF) == 0) { /* not buffered? */
242 drm_err = 1; /* error */
243 CRETIOE (drm_stopioe, SCPE_UNATT);
244 }
245 if (drm_rw) { /* write? */
246 dwd = M[drm_ca]; /* get mem word */
247 fbuf[drm_da] = dwd; /* write to drum */
248 if (drm_da >= uptr->hwmark) uptr->hwmark = drm_da + 1;
249 }
250 else { /* read */
251 dwd = fbuf[drm_da]; /* get drum word */
252 M[drm_ca] = dwd; /* write to mem */
253 }
254 drm_da = drm_da + 1; /* inc drum addr */
255 if (drm_da >= DRM_SIZE) drm_da = 0; /* wrap */
256 drm_ca = (drm_ca + 1) & PAMASK; /* inc core addr */
257 drm_wc = (drm_wc - 1) & DRM_WCMASK; /* dec word cnt */
258 drm_par = drm_par ^ (dwd >> 12); /* parity */
259 drm_par = ((drm_par << 1) | (drm_par >> 11)) & 07777;
260 drm_par = drm_par ^ (dwd & 07777);
261 if (drm_wc) { /* more to do */
262 if (drm_da & DRM_M_WD) sim_activate (uptr, drm_xtime);
263 else sim_activate (uptr, drm_xtime * DRM_NUMGP);
264 }
265 else { /* end xfr */
266 #if defined (DRM_PAR)
267 if ((drm_da & DRM_M_WD) && drm_rw) { /* wr end mid sector? */
268 M[drm_da] = drm_par << 12; /* clobber data */
269 if (drm_da >= uptr->hwmark) uptr->hwmark = drm_da + 1;
270 }
271 #endif
272 drm_sta = DRM_SFET; /* back to fetch */
273 sim_activate (uptr, drm_ftime); /* schedule */
274 } /* end else end xfr */
275 } /* end else xfr */
276 return SCPE_OK;
277 }
278
279 /* Reset routine */
280
281 t_stat drm_reset (DEVICE *dptr)
282 {
283 drm_da = 0; /* clear state */
284 drm_ca = 0;
285 drm_wc = 0;
286 drm_par = 0;
287 drm_sta = 0;
288 drm_err = 0;
289 drm_rw = 0;
290 int_req = int_req & ~INT_DRM; /* clear intr */
291 sim_cancel (&drm_unit); /* deactivate */
292 return SCPE_OK;
293 }