First Commit of my working state
[simh.git] / I7094 / i7094_drm.c
1 /* i7094_drm.c: 7289/7320A drum simulator
2
3 Copyright (c) 2005-2006, 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 Very little is known about this device; the behavior simulated here is
29 what is used by CTSS.
30
31 - The drum channel/controller behaves like a hybrid of the 7607 and the 7909.
32 It responds to SCD (like the 7909), gets its address from the channel
33 program (like the 7909), but responds to IOCD/IOCP (like the 7607) and
34 sets channel flags (like the 7607).
35 - The drum channel supports at least 2 drums. The maximum is 8 or less.
36 Physical drums are numbered from 0.
37 - Each drum has a capacity of 192K 36b words. This is divided into 6
38 "logical" drum of 32KW each. Each "logical" drum has 16 2048W "sectors".
39 Logical drums are numbered from 1.
40 - The drum's behavior if a sector boundary is crossed in mid-transfer is
41 unknown. CTSS never does this.
42 - The drum's behavior with record operations is unknown. CTSS only uses
43 IOCD and IOCP.
44 - The drum's error indicators are unknown. CTSS regards bits <0:2,13> of
45 the returned SCD data as errors, as well as the normal 7607 trap flags.
46 - The drum's rotational speed is unknown.
47
48 Assumptions in this simulator:
49
50 - Transfers may not cross a sector boundary. An attempt to do so sets
51 the EOF flag and causes an immediate disconnect.
52 - The hardware never sets end of record.
53
54 For speed, the entire drum is buffered in memory.
55 */
56
57 #include "i7094_defs.h"
58 #include <math.h>
59
60 #define DRM_NUMDR 8 /* drums/controller */
61
62 /* Drum geometry */
63
64 #define DRM_NUMWDS 2048 /* words/sector */
65 #define DRM_SCMASK (DRM_NUMWDS - 1) /* sector mask */
66 #define DRM_NUMSC 16 /* sectors/log drum */
67 #define DRM_NUMWDL (DRM_NUMWDS * DRM_NUMSC) /* words/log drum */
68 #define DRM_NUMLD 6 /* log drums/phys drum */
69 #define DRM_SIZE (DRM_NUMLD * DRM_NUMWDL) /* words/phys drum */
70 #define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \
71 ((double) DRM_NUMWDS)))
72
73 /* Drum address from channel */
74
75 #define DRM_V_PHY 30 /* physical drum sel */
76 #define DRM_M_PHY 07
77 #define DRM_V_LOG 18 /* logical drum sel */
78 #define DRM_M_LOG 07
79 #define DRM_V_WDA 0 /* word address */
80 #define DRM_M_WDA (DRM_NUMWDL - 1)
81 #define DRM_GETPHY(x) (((uint32) ((x) >> DRM_V_PHY)) & DRM_M_PHY)
82 #define DRM_GETLOG(x) ((((uint32) (x)) >> DRM_V_LOG) & DRM_M_LOG)
83 #define DRM_GETWDA(x) ((((uint32) (x)) >> DRM_V_WDA) & DRM_M_WDA)
84 #define DRM_GETDA(x) (((DRM_GETLOG(x) - 1) * DRM_NUMWDL) + DRM_GETWDA(x))
85
86 /* Drum controller states */
87
88 #define DRM_IDLE 0
89 #define DRM_1ST 1
90 #define DRM_DATA 2
91 #define DRM_EOS 3
92
93 uint32 drm_ch = CH_G; /* drum channel */
94 uint32 drm_da = 0; /* drum address */
95 uint32 drm_sta = 0; /* state */
96 uint32 drm_op = 0; /* operation */
97 t_uint64 drm_chob = 0; /* output buf */
98 uint32 drm_chob_v = 0; /* valid */
99 int32 drm_time = 10; /* inter-word time */
100
101 extern uint32 ind_ioc;
102
103 t_stat drm_svc (UNIT *uptr);
104 t_stat drm_reset (DEVICE *dptr);
105 t_stat drm_chsel (uint32 ch, uint32 sel, uint32 unit);
106 t_stat drm_chwr (uint32 ch, t_uint64 val, uint32 flags);
107 t_bool drm_da_incr (void);
108
109 /* DRM data structures
110
111 drm_dev DRM device descriptor
112 drm_unit DRM unit descriptor
113 drm_reg DRM register list
114 */
115
116 DIB drm_dib = { &drm_chsel, &drm_chwr };
117
118 UNIT drm_unit[] = {
119 { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+
120 UNIT_MUSTBUF+UNIT_DISABLE, DRM_SIZE) },
121 { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+
122 UNIT_MUSTBUF+UNIT_DISABLE, DRM_SIZE) },
123 { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+
124 UNIT_MUSTBUF+UNIT_DISABLE+UNIT_DIS, DRM_SIZE) },
125 { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+
126 UNIT_MUSTBUF+UNIT_DISABLE+UNIT_DIS, DRM_SIZE) },
127 { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+
128 UNIT_MUSTBUF+UNIT_DISABLE+UNIT_DIS, DRM_SIZE) },
129 { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+
130 UNIT_MUSTBUF+UNIT_DISABLE+UNIT_DIS, DRM_SIZE) },
131 { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+
132 UNIT_MUSTBUF+UNIT_DISABLE+UNIT_DIS, DRM_SIZE) },
133 { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+
134 UNIT_MUSTBUF+UNIT_DISABLE+UNIT_DIS, DRM_SIZE) }
135 };
136
137 REG drm_reg[] = {
138 { ORDATA (STATE, drm_sta, 2) },
139 { ORDATA (DA, drm_da, 18) },
140 { FLDATA (OP, drm_op, 0) },
141 { ORDATA (CHOB, drm_chob, 36) },
142 { FLDATA (CHOBV, drm_chob_v, 0) },
143 { DRDATA (TIME, drm_time, 24), REG_NZ + PV_LEFT },
144 { DRDATA (CHAN, drm_ch, 3), REG_HRO },
145 { NULL }
146 };
147
148 MTAB drm_mtab[] = {
149 { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", NULL, NULL, &ch_show_chan },
150 { 0 }
151 };
152
153 DEVICE drm_dev = {
154 "DRM", drm_unit, drm_reg, drm_mtab,
155 DRM_NUMDR, 8, 18, 1, 8, 36,
156 NULL, NULL, &drm_reset,
157 NULL, NULL, NULL,
158 &drm_dib, DEV_DIS
159 };
160
161 /* Channel select routine */
162
163 t_stat drm_chsel (uint32 ch, uint32 sel, uint32 unit)
164 {
165 drm_ch = ch; /* save channel */
166 if (sel & CHSL_NDS) return ch6_end_nds (ch); /* nds? nop */
167
168 switch (sel) { /* case on cmd */
169
170 case CHSL_RDS: /* read */
171 case CHSL_WRS: /* write */
172 if (drm_sta != DRM_IDLE) return ERR_STALL; /* busy? */
173 drm_sta = DRM_1ST; /* initial state */
174 if (sel == CHSL_WRS) drm_op = 1; /* set read/write */
175 else drm_op = 0; /* LCHx sends addr */
176 break; /* wait for addr */
177
178 default: /* other */
179 return STOP_ILLIOP;
180 }
181 return SCPE_OK;
182 }
183
184 /* Channel write routine */
185
186 t_stat drm_chwr (uint32 ch, t_uint64 val, uint32 flags)
187 {
188 uint32 u, l;
189 int32 cp, dp;
190
191 if (drm_sta == DRM_1ST) {
192 u = DRM_GETPHY (val); /* get unit */
193 l = DRM_GETLOG (val); /* get logical address */
194 if ((u >= DRM_NUMDR) || /* invalid unit? */
195 (drm_unit[u].flags & UNIT_DIS) || /* disabled unit? */
196 (l == 0) || (l > DRM_NUMLD)) { /* invalid log drum? */
197 ch6_err_disc (ch, U_DRM, CHF_TRC); /* disconnect */
198 drm_sta = DRM_IDLE;
199 return SCPE_OK;
200 }
201 drm_da = DRM_GETDA (val); /* get drum addr */
202 cp = GET_POS (drm_time); /* current pos in sec */
203 dp = (drm_da & DRM_SCMASK) - cp; /* delta to desired pos */
204 if (dp <= 0) dp = dp + DRM_NUMWDS; /* if neg, add rev */
205 sim_activate (&drm_unit[u], dp * drm_time); /* schedule */
206 if (drm_op) ch6_req_wr (ch, U_DRM); /* if write, get word */
207 drm_sta = DRM_DATA;
208 drm_chob = 0; /* clr, inval buffer */
209 drm_chob_v = 0;
210 }
211 else {
212 drm_chob = val & DMASK;
213 drm_chob_v = 1;
214 }
215 return SCPE_OK;
216 }
217
218 /* Unit service - this code assumes the entire drum is buffered */
219
220 t_stat drm_svc (UNIT *uptr)
221 {
222 t_uint64 *fbuf = (t_uint64 *) uptr->filebuf;
223
224 if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? */
225 ch6_err_disc (drm_ch, U_DRM, CHF_TRC); /* set TRC, disc */
226 drm_sta = DRM_IDLE; /* drum is idle */
227 return SCPE_UNATT;
228 }
229 if (drm_da >= DRM_SIZE) { /* nx logical drum? */
230 ch6_err_disc (drm_ch, U_DRM, CHF_EOF); /* set EOF, disc */
231 drm_sta = DRM_IDLE; /* drum is idle */
232 return SCPE_OK;
233 }
234
235 switch (drm_sta) { /* case on state */
236
237 case DRM_DATA: /* data */
238 if (drm_op) { /* write? */
239 if (drm_chob_v) drm_chob_v = 0; /* valid? clear */
240 else if (ch6_qconn (drm_ch, U_DRM)) /* no, chan conn? */
241 ind_ioc = 1; /* io check */
242 fbuf[drm_da] = drm_chob; /* get data */
243 if (drm_da >= uptr->hwmark) uptr->hwmark = drm_da + 1;
244 if (!drm_da_incr ()) ch6_req_wr (drm_ch, U_DRM);
245 }
246 else{ /* read */
247 ch6_req_rd (drm_ch, U_DRM, fbuf[drm_da], 0); /* send word to channel */
248 drm_da_incr ();
249 }
250 sim_activate (uptr, drm_time); /* next word */
251 break;
252
253 case DRM_EOS: /* end sector */
254 if (ch6_qconn (drm_ch, U_DRM)) /* drum still conn? */
255 ch6_err_disc (drm_ch, U_DRM, CHF_EOF); /* set EOF, disc */
256 drm_sta = DRM_IDLE; /* drum is idle */
257 break;
258 } /* end case */
259
260 return SCPE_OK;
261 }
262
263 /* Increment drum address - return true, set new state if end of sector */
264
265 t_bool drm_da_incr (void)
266 {
267 drm_da = drm_da + 1;
268 if (drm_da & DRM_SCMASK) return FALSE;
269 drm_sta = DRM_EOS;
270 return TRUE;
271 }
272
273 /* Reset routine */
274
275 t_stat drm_reset (DEVICE *dptr)
276 {
277 uint32 i;
278
279 drm_da = 0;
280 drm_op = 0;
281 drm_sta = DRM_IDLE;
282 drm_chob = 0;
283 drm_chob_v = 0;
284 for (i = 0; i < dptr->numunits; i++) sim_cancel (dptr->units + i);
285 return SCPE_OK;
286 }