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