1 /* i7094_drm.c: 7289/7320A drum simulator
2
3 Copyright (c) 2005-2011, 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 7289/7320A "fast" drum
27
28 23-Mar-12 RMS Corrected disk addressing and logical disk crossing
29 25-Mar-11 RMS Updated based on RPQ
30
31 This simulator implements a subset of the functionality of the 7289, as
32 required by CTSS.
33
34 - The drum channel/controller behaves like a hybrid of the 7607 and the 7909.
35 It responds to SCD (like the 7909), gets its address from the channel
36 program (like the 7909), but responds to IOCD/IOCP (like the 7607) and
37 sets channel flags (like the 7607).
38 - The drum channel supports at least 2 drums. The maximum is 4 or less.
39 Physical drums are numbered from 0.
40 - Each drum has a capacity of 192K 36b words. This is divided into 6
41 "logical" drum of 32KW each. Each "logical" drum has 16 2048W "sectors".
42 Logical drums are numbered from 1.
43 - The drum allows transfers across sector boundaries, but not logical
44 drum boundaries.
45 - The drum's only supports IOCD, IOCP, and IOCT. IOCT (and chaining mode)
46 are not used by CTSS.
47
48 Limitations in this simulator:
49
50 - Chain mode is not implemented.
51 - LPCR is not implemented.
52
53 For speed, the entire drum is buffered in memory.
54 */
55
56 #include "i7094_defs.h"
57 #include <math.h>
58
59 #define DRM_NUMDR 4 /* drums/controller */
60
61 /* Drum geometry */
62
63 #define DRM_NUMWDG 1024 /* words/group */
64 #define DRM_GPMASK (DRM_NUMWDG - 1) /* group mask */
65 #define DRM_NUMWDS 2048 /* words/sector */
66 #define DRM_SCMASK (DRM_NUMWDS - 1) /* sector mask */
67 #define DRM_NUMSC 16 /* sectors/log drum */
68 #define DRM_NUMWDL (DRM_NUMWDS * DRM_NUMSC) /* words/log drum */
69 #define DRM_LDMASK (DRM_NUMWDL - 1) /* logical disk mask */
70 #define DRM_NUMLD 6 /* log drums/phys drum */
71 #define DRM_SIZE (DRM_NUMLD * DRM_NUMWDL) /* words/phys drum */
72 #define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \
73 ((double) DRM_NUMWDS)))
74 #define GET_PROT(x) ((x[drm_phy] >> (drm_log - 1)) & 1)
75
76 /* Drum address from channel */
77
78 #define DRM_V_PHY 30 /* physical drum sel */
79 #define DRM_M_PHY 03
80 #define DRM_V_LOG 18 /* logical drum sel */
81 #define DRM_M_LOG 07
82 #define DRM_V_WDA 0 /* word address */
83 #define DRM_M_WDA (DRM_NUMWDL - 1)
84 #define DRM_GETPHY(x) (((uint32) ((x) >> DRM_V_PHY)) & DRM_M_PHY)
85 #define DRM_GETLOG(x) ((((uint32) (x)) >> DRM_V_LOG) & DRM_M_LOG)
86 #define DRM_GETWDA(x) ((((uint32) (x)) >> DRM_V_WDA) & DRM_M_WDA)
87 #define DRM_GETDA(l,x) ((((l) - 1) * DRM_NUMWDL) + (x))
88
89 /* SCD word */
90
91 #define DRMS_V_IOC 35 /* IO check */
92 #define DRMS_V_INV 33 /* invalid command */
93 #define DRMS_V_PHY 31 /* physical drum */
94 #define DRMS_V_LOG 28 /* logical drum */
95 #define DRMS_V_WDA 13 /* disk address */
96 #define DRMS_V_WRP 22 /* write protect */
97 #define DRMS_V_LPCR 18 /* LPRCR */
98 #define DRMS_M_LPCR 017
99
100 /* Drum controller states */
101
102 #define DRM_IDLE 0
103 #define DRM_1ST 1
104 #define DRM_FILL 2
105 #define DRM_DATA 3
106 #define DRM_EOD 4
107
108 uint32 drm_ch = CH_G; /* drum channel */
109 uint32 drm_da = 0; /* drum address */
110 uint32 drm_phy = 0; /* physical drum */
111 uint32 drm_log = 0; /* logical drum */
112 uint32 drm_sta = 0; /* state */
113 uint32 drm_op = 0; /* operation */
114 t_uint64 drm_chob = 0; /* output buf */
115 uint32 drm_chob_v = 0; /* valid */
116 uint32 drm_prot[DRM_NUMDR] = { 0 }; /* drum protect sw */
117 int32 drm_time = 10; /* inter-word time */
118
119 extern uint32 ind_ioc;
120
121 t_stat drm_svc (UNIT *uptr);
122 t_stat drm_reset (DEVICE *dptr);
123 t_stat drm_chsel (uint32 ch, uint32 sel, uint32 unit);
124 t_stat drm_chwr (uint32 ch, t_uint64 val, uint32 flags);
125 t_bool drm_da_incr (void);
126
127 /* DRM data structures
128
129 drm_dev DRM device descriptor
130 drm_unit DRM unit descriptor
131 drm_reg DRM register list
132 */
133
134 DIB drm_dib = { &drm_chsel, &drm_chwr };
135
136 UNIT drm_unit[] = {
137 { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+
138 UNIT_MUSTBUF+UNIT_DISABLE, DRM_SIZE) },
139 { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+
140 UNIT_MUSTBUF+UNIT_DISABLE, DRM_SIZE) },
141 { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+
142 UNIT_MUSTBUF+UNIT_DISABLE+UNIT_DIS, DRM_SIZE) },
143 { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+
144 UNIT_MUSTBUF+UNIT_DISABLE+UNIT_DIS, DRM_SIZE) },
145 };
146
147 REG drm_reg[] = {
148 { ORDATA (STATE, drm_sta, 3) },
149 { ORDATA (UNIT,drm_phy, 2), REG_RO },
150 { ORDATA (LOG, drm_log, 3), REG_RO },
151 { ORDATA (DA, drm_da, 15) },
152 { FLDATA (OP, drm_op, 0) },
153 { ORDATA (CHOB, drm_chob, 36) },
154 { FLDATA (CHOBV, drm_chob_v, 0) },
155 { ORDATA (PROT0, drm_prot[0], 6) },
156 { ORDATA (PROT1, drm_prot[1], 6) },
157 { ORDATA (PROT2, drm_prot[2], 6) },
158 { ORDATA (PROT3, drm_prot[3], 6) },
159 { DRDATA (TIME, drm_time, 24), REG_NZ + PV_LEFT },
160 { DRDATA (CHAN, drm_ch, 3), REG_HRO },
161 { NULL }
162 };
163
164 MTAB drm_mtab[] = {
165 { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", NULL, NULL, &ch_show_chan },
166 { 0 }
167 };
168
169 DEVICE drm_dev = {
170 "DRM", drm_unit, drm_reg, drm_mtab,
171 DRM_NUMDR, 8, 18, 1, 8, 36,
172 NULL, NULL, &drm_reset,
173 NULL, NULL, NULL,
174 &drm_dib, DEV_DIS
175 };
176
177 /* Channel select routine */
178
drm_chsel(uint32 ch,uint32 sel,uint32 unit)179 t_stat drm_chsel (uint32 ch, uint32 sel, uint32 unit)
180 {
181 drm_ch = ch; /* save channel */
182 if (sel & CHSL_NDS) /* nds? nop */
183 return ch6_end_nds (ch);
184
185 switch (sel) { /* case on cmd */
186
187 case CHSL_RDS: /* read */
188 case CHSL_WRS: /* write */
189 if (drm_sta != DRM_IDLE) /* busy? */
190 return ERR_STALL;
191 drm_sta = DRM_1ST; /* initial state */
192 if (sel == CHSL_WRS) /* set read/write */
193 drm_op = 1;
194 else drm_op = 0; /* LCHx sends addr */
195 break; /* wait for addr */
196
197 default: /* other */
198 return STOP_ILLIOP;
199 }
200 return SCPE_OK;
201 }
202
203 /* Channel diagnostic store routine */
204
drm_sdc(uint32 ch)205 t_uint64 drm_sdc (uint32 ch)
206 {
207 t_uint64 val;
208
209
210 val = (((t_uint64) ind_ioc) << DRMS_V_IOC) |
211 (((t_uint64) drm_phy) << DRMS_V_PHY) |
212 (((t_uint64) drm_log) << DRMS_V_LOG) |
213 (((t_uint64) (drm_da & ~ DRM_GPMASK)) << DRMS_V_WDA) |
214 (((t_uint64) GET_PROT(drm_prot)) << DRMS_V_WRP);
215 return val;
216 }
217
218 /* Channel write routine */
219
drm_chwr(uint32 ch,t_uint64 val,uint32 flags)220 t_stat drm_chwr (uint32 ch, t_uint64 val, uint32 flags)
221 {
222 int32 cp, dp;
223
224 if (drm_sta == DRM_1ST) {
225 drm_phy = DRM_GETPHY (val); /* get unit */
226 drm_log = DRM_GETLOG (val); /* get logical disk */
227 drm_da = DRM_GETWDA (val); /* get drum word addr */
228 if ((drm_unit[drm_phy].flags & UNIT_DIS) || /* disabled unit? */
229 (drm_log == 0) || (drm_log > DRM_NUMLD) || /* invalid log drum? */
230 ((drm_op != 0) && (GET_PROT (drm_prot) != 0))) { /* write to prot drum? */
231 ch6_err_disc (ch, U_DRM, CHF_TRC); /* disconnect */
232 drm_sta = DRM_IDLE;
233 return SCPE_OK;
234 }
235 cp = GET_POS (drm_time); /* current pos in sec */
236 dp = (drm_da & DRM_SCMASK) - cp; /* delta to desired pos */
237 if (dp <= 0) /* if neg, add rev */
238 dp = dp + DRM_NUMWDS;
239 sim_activate (&drm_unit[drm_phy], dp * drm_time); /* schedule */
240 if (drm_op) { /* if write, get word */
241 ch6_req_wr (ch, U_DRM);
242 drm_sta = DRM_FILL; /* sector fill */
243 }
244 else drm_sta = DRM_DATA; /* data transfer */
245 drm_chob = 0; /* clr, inval buffer */
246 drm_chob_v = 0;
247 }
248 else {
249 drm_chob = val & DMASK;
250 drm_chob_v = 1;
251 }
252 return SCPE_OK;
253 }
254
255 /* Unit service - this code assumes the entire drum is buffered */
256
drm_svc(UNIT * uptr)257 t_stat drm_svc (UNIT *uptr)
258 {
259 uint32 i;
260 t_uint64 *fbuf = (t_uint64 *) uptr->filebuf;
261 uint32 da = DRM_GETDA (drm_log, drm_da);
262
263 if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? */
264 ch6_err_disc (drm_ch, U_DRM, CHF_TRC); /* set TRC, disc */
265 drm_sta = DRM_IDLE; /* drum is idle */
266 return SCPE_UNATT;
267 }
268
269 switch (drm_sta) { /* case on state */
270
271 case DRM_FILL: /* write, clr group */
272 for (i = da & ~DRM_GPMASK; i <= (da | DRM_GPMASK); i++)
273 fbuf[i] = 0; /* clear group */
274 if (i >= uptr-> hwmark)
275 uptr->hwmark = i + 1;
276 drm_sta = DRM_DATA; /* now data */
277 /* fall through */
278 case DRM_DATA: /* data */
279 if (drm_op) { /* write? */
280 if (drm_chob_v) /* valid? clear */
281 drm_chob_v = 0;
282 else if (ch6_qconn (drm_ch, U_DRM)) /* no, chan conn? */
283 ind_ioc = 1; /* io check */
284 fbuf[da] = drm_chob; /* get data */
285 if (da >= uptr->hwmark)
286 uptr->hwmark = da + 1;
287 if (!drm_da_incr ()) /* room for more? */
288 ch6_req_wr (drm_ch, U_DRM);
289 }
290 else{ /* read */
291 ch6_req_rd (drm_ch, U_DRM, fbuf[da], 0); /* send word to channel */
292 drm_da_incr ();
293 }
294 sim_activate (uptr, drm_time); /* next word */
295 break;
296
297 case DRM_EOD: /* end logical disk */
298 if (ch6_qconn (drm_ch, U_DRM)) /* drum still conn? */
299 ch6_err_disc (drm_ch, U_DRM, CHF_EOF); /* set EOF, disc */
300 drm_sta = DRM_IDLE; /* drum is idle */
301 break;
302 } /* end case */
303
304 return SCPE_OK;
305 }
306
307 /* Increment drum address - return true, set new state if end of logical disk */
308
drm_da_incr(void)309 t_bool drm_da_incr (void)
310 {
311 drm_da = (drm_da + 1) & DRM_LDMASK;
312 if (drm_da != 0)
313 return FALSE;
314 drm_sta = DRM_EOD;
315 return TRUE;
316 }
317
318 /* Reset routine */
319
drm_reset(DEVICE * dptr)320 t_stat drm_reset (DEVICE *dptr)
321 {
322 uint32 i;
323
324 drm_phy = 0;
325 drm_log = 0;
326 drm_da = 0;
327 drm_op = 0;
328 drm_sta = DRM_IDLE;
329 drm_chob = 0;
330 drm_chob_v = 0;
331 for (i = 0; i < dptr->numunits; i++)
332 sim_cancel (dptr->units + i);
333 return SCPE_OK;
334 }
335