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