| 1 | /* pdp1_drm.c: PDP-1 drum simulator\r |
| 2 | \r |
| 3 | Copyright (c) 1993-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 | drp Type 23 parallel drum\r |
| 27 | drm Type 24 serial drum\r |
| 28 | \r |
| 29 | 21-Dec-06 RMS Added 16-chan SBS support\r |
| 30 | 08-Dec-03 RMS Added parallel drum support\r |
| 31 | Fixed bug in DBL/DCN decoding\r |
| 32 | 26-Oct-03 RMS Cleaned up buffer copy code\r |
| 33 | 23-Jul-03 RMS Fixed incorrect logical, missing activate\r |
| 34 | 05-Dec-02 RMS Cloned from pdp18b_drm.c\r |
| 35 | */\r |
| 36 | \r |
| 37 | #include "pdp1_defs.h"\r |
| 38 | #include <math.h>\r |
| 39 | \r |
| 40 | /* Serial drum constants */\r |
| 41 | \r |
| 42 | #define DRM_NUMWDS 256 /* words/sector */\r |
| 43 | #define DRM_NUMSC 2 /* sectors/track */\r |
| 44 | #define DRM_NUMTR 256 /* tracks/drum */\r |
| 45 | #define DRM_NUMWDT (DRM_NUMWDS * DRM_NUMSC) /* words/track */\r |
| 46 | #define DRM_SIZE (DRM_NUMTR * DRM_NUMWDT) /* words/drum */\r |
| 47 | #define DRM_SMASK ((DRM_NUMTR * DRM_NUMSC) - 1) /* sector mask */\r |
| 48 | \r |
| 49 | /* Parallel drum constants */\r |
| 50 | \r |
| 51 | #define DRP_NUMWDT 4096 /* words/track */\r |
| 52 | #define DRP_NUMTK 32 /* tracks/drum */\r |
| 53 | #define DRP_SIZE (DRP_NUMWDT * DRP_NUMTK) /* words/drum */\r |
| 54 | #define DRP_V_RWE 17 /* read/write enable */\r |
| 55 | #define DRP_V_FLD 12 /* drum field */\r |
| 56 | #define DRP_M_FLD 037\r |
| 57 | #define DRP_TAMASK 07777 /* track address */\r |
| 58 | #define DRP_WCMASK 07777 /* word count */\r |
| 59 | #define DRP_MAINCM 07777 /* mem addr incr */\r |
| 60 | #define DRP_GETRWE(x) (((x) >> DRP_V_RWE) & 1)\r |
| 61 | #define DRP_GETRWF(x) (((x) >> DRP_V_FLD) & DRP_M_FLD)\r |
| 62 | \r |
| 63 | /* Parameters in the unit descriptor */\r |
| 64 | \r |
| 65 | #define FUNC u4 /* function */\r |
| 66 | #define DRM_READ 000 /* read */\r |
| 67 | #define DRM_WRITE 010 /* write */\r |
| 68 | #define DRP_RW 000 /* read/write */\r |
| 69 | #define DRP_BRK 001 /* break on address */\r |
| 70 | \r |
| 71 | #define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \\r |
| 72 | ((double) DRM_NUMWDT)))\r |
| 73 | \r |
| 74 | extern int32 M[];\r |
| 75 | extern int32 iosta;\r |
| 76 | extern int32 stop_inst;\r |
| 77 | extern UNIT cpu_unit;\r |
| 78 | \r |
| 79 | /* Serial drum variables */\r |
| 80 | \r |
| 81 | uint32 drm_da = 0; /* track address */\r |
| 82 | uint32 drm_ma = 0; /* memory address */\r |
| 83 | uint32 drm_err = 0; /* error flag */\r |
| 84 | uint32 drm_wlk = 0; /* write lock */\r |
| 85 | int32 drm_time = 4; /* inter-word time */\r |
| 86 | int32 drm_sbs = 0; /* SBS level */\r |
| 87 | int32 drm_stopioe = 1; /* stop on error */\r |
| 88 | \r |
| 89 | /* Parallel drum variables */\r |
| 90 | \r |
| 91 | uint32 drp_rde = 0; /* read enable */\r |
| 92 | uint32 drp_wre = 0; /* write enable */\r |
| 93 | uint32 drp_rdf = 0; /* read field */\r |
| 94 | uint32 drp_wrf = 0; /* write field */\r |
| 95 | uint32 drp_ta = 0; /* track address */\r |
| 96 | uint32 drp_wc = 0; /* word count */\r |
| 97 | uint32 drp_ma = 0; /* memory address */\r |
| 98 | uint32 drp_err = 0; /* error */\r |
| 99 | int32 drp_time = 2; /* inter-word time */\r |
| 100 | int32 drp_stopioe = 1; /* stop on error */\r |
| 101 | \r |
| 102 | /* Forward declarations */\r |
| 103 | \r |
| 104 | t_stat drm_svc (UNIT *uptr);\r |
| 105 | t_stat drm_reset (DEVICE *dptr);\r |
| 106 | t_stat drp_svc (UNIT *uptr);\r |
| 107 | t_stat drp_reset (DEVICE *dptr);\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 | UNIT drm_unit = {\r |
| 117 | UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF,\r |
| 118 | DRM_SIZE)\r |
| 119 | };\r |
| 120 | \r |
| 121 | REG drm_reg[] = {\r |
| 122 | { ORDATA (DA, drm_da, 9) },\r |
| 123 | { ORDATA (MA, drm_ma, 16) },\r |
| 124 | { FLDATA (DONE, iosta, IOS_V_DRM) },\r |
| 125 | { FLDATA (ERR, drm_err, 0) },\r |
| 126 | { ORDATA (WLK, drm_wlk, 32) },\r |
| 127 | { DRDATA (TIME, drm_time, 24), REG_NZ + PV_LEFT },\r |
| 128 | { DRDATA (SBSLVL, drm_sbs, 4), REG_HRO },\r |
| 129 | { FLDATA (STOP_IOE, drm_stopioe, 0) },\r |
| 130 | { NULL }\r |
| 131 | };\r |
| 132 | \r |
| 133 | MTAB drm_mod[] = {\r |
| 134 | { MTAB_XTD|MTAB_VDV, 0, "APILVL", "APILVL",\r |
| 135 | &dev_set_sbs, &dev_show_sbs, (void *) &drm_sbs },\r |
| 136 | { 0 }\r |
| 137 | };\r |
| 138 | \r |
| 139 | DEVICE drm_dev = {\r |
| 140 | "DRM", &drm_unit, drm_reg, drm_mod,\r |
| 141 | 1, 8, 20, 1, 8, 18,\r |
| 142 | NULL, NULL, &drm_reset,\r |
| 143 | NULL, NULL, NULL,\r |
| 144 | NULL, DEV_DISABLE\r |
| 145 | };\r |
| 146 | \r |
| 147 | /* DRP data structures\r |
| 148 | \r |
| 149 | drp_dev DRP device descriptor\r |
| 150 | drp_unit DRP unit descriptor\r |
| 151 | drp_reg DRP register list\r |
| 152 | */\r |
| 153 | \r |
| 154 | UNIT drp_unit = {\r |
| 155 | UDATA (&drp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF,\r |
| 156 | DRM_SIZE)\r |
| 157 | };\r |
| 158 | \r |
| 159 | REG drp_reg[] = {\r |
| 160 | { ORDATA (TA, drp_ta, 12) },\r |
| 161 | { ORDATA (RDF, drp_rdf, 5) },\r |
| 162 | { FLDATA (RDE, drp_rde, 0) },\r |
| 163 | { FLDATA (WRF, drp_wrf, 5) },\r |
| 164 | { FLDATA (WRE, drp_wre, 0) },\r |
| 165 | { ORDATA (MA, drp_ma, 16) },\r |
| 166 | { ORDATA (WC, drp_wc, 12) },\r |
| 167 | { FLDATA (BUSY, iosta, IOS_V_DRP) },\r |
| 168 | { FLDATA (ERR, drp_err, 0) },\r |
| 169 | { DRDATA (TIME, drp_time, 24), REG_NZ + PV_LEFT },\r |
| 170 | { FLDATA (STOP_IOE, drp_stopioe, 0) },\r |
| 171 | { DRDATA (SBSLVL, drm_sbs, 4), REG_HRO },\r |
| 172 | { NULL }\r |
| 173 | };\r |
| 174 | \r |
| 175 | DEVICE drp_dev = {\r |
| 176 | "DRP", &drp_unit, drp_reg, NULL,\r |
| 177 | 1, 8, 20, 1, 8, 18,\r |
| 178 | NULL, NULL, &drp_reset,\r |
| 179 | NULL, NULL, NULL,\r |
| 180 | NULL, DEV_DISABLE | DEV_DIS\r |
| 181 | };\r |
| 182 | \r |
| 183 | /* IOT routines */\r |
| 184 | \r |
| 185 | int32 drm (int32 IR, int32 dev, int32 dat)\r |
| 186 | {\r |
| 187 | int32 t;\r |
| 188 | int32 pulse = (IR >> 6) & 037;\r |
| 189 | \r |
| 190 | if ((drm_dev.flags & DEV_DIS) == 0) { /* serial enabled? */\r |
| 191 | if ((pulse != 001) && (pulse != 011)) /* invalid pulse? */\r |
| 192 | return (stop_inst << IOT_V_REASON) | dat; /* stop if requested */\r |
| 193 | switch (dev) { /* switch on device */\r |
| 194 | \r |
| 195 | case 061: /* DWR, DRD */\r |
| 196 | drm_ma = dat & AMASK; /* load mem addr */\r |
| 197 | drm_unit.FUNC = pulse & DRM_WRITE; /* save function */\r |
| 198 | break;\r |
| 199 | \r |
| 200 | case 062: /* DBL, DCN */\r |
| 201 | if ((pulse & 010) == 0) /* DBL? */\r |
| 202 | drm_da = dat & DRM_SMASK; /* load sector # */\r |
| 203 | iosta = iosta & ~IOS_DRM; /* clear flags */\r |
| 204 | drm_err = 0;\r |
| 205 | t = ((drm_da % DRM_NUMSC) * DRM_NUMWDS) - GET_POS (drm_time);\r |
| 206 | if (t <= 0) t = t + DRM_NUMWDT; /* wrap around? */\r |
| 207 | sim_activate (&drm_unit, t); /* start operation */\r |
| 208 | break;\r |
| 209 | \r |
| 210 | case 063: /* DTD */\r |
| 211 | if (pulse == 011) return (stop_inst << IOT_V_REASON) | dat;\r |
| 212 | if (iosta & IOS_DRM) return (dat | IOT_SKP); /* skip if done */\r |
| 213 | break;\r |
| 214 | \r |
| 215 | case 064: /* DSE, DSP */\r |
| 216 | if ((drm_err == 0) || (pulse & 010)) /* no error, par test? */\r |
| 217 | return (dat | IOT_SKP);\r |
| 218 | } /* end case */\r |
| 219 | \r |
| 220 | return dat;\r |
| 221 | } /* end if serial */\r |
| 222 | \r |
| 223 | if ((drp_dev.flags & DEV_DIS) == 0) { /* parallel enabled? */\r |
| 224 | switch (dev) { /* switch on device */\r |
| 225 | \r |
| 226 | case 061: /* DIA, DBA */\r |
| 227 | drp_err = 0; /* clear error */\r |
| 228 | iosta = iosta & ~IOS_DRP; /* not busy */\r |
| 229 | drp_rde = DRP_GETRWE (dat); /* set read enable */\r |
| 230 | drp_rdf = DRP_GETRWF (dat); /* set read field */\r |
| 231 | drp_ta = dat & DRP_TAMASK; /* set track addr */\r |
| 232 | if (IR & 02000) { /* DBA? */\r |
| 233 | t = drp_ta - GET_POS (drp_time); /* delta words */\r |
| 234 | if (t <= 0) t = t + DRP_NUMWDT; /* wrap around? */\r |
| 235 | sim_activate (&drp_unit, t); /* start operation */\r |
| 236 | drp_unit.FUNC = DRP_BRK; /* mark as break */\r |
| 237 | }\r |
| 238 | else drp_unit.FUNC = DRP_RW; /* no, read/write */\r |
| 239 | break;\r |
| 240 | \r |
| 241 | case 062: /* DWC, DRA */\r |
| 242 | if (IR & 02000) dat = GET_POS (drp_time) | /* DRA, get position */\r |
| 243 | (drp_err? 0400000: 0);\r |
| 244 | else { /* DWC */\r |
| 245 | drp_wre = DRP_GETRWE (dat); /* set write enable */\r |
| 246 | drp_wrf = DRP_GETRWF (dat); /* set write field */\r |
| 247 | drp_wc = dat & DRP_WCMASK; /* set word count */\r |
| 248 | }\r |
| 249 | break;\r |
| 250 | \r |
| 251 | case 063: /* DCL */\r |
| 252 | drp_ma = dat & AMASK; /* set mem address */\r |
| 253 | t = drp_ta - GET_POS (drp_time); /* delta words */\r |
| 254 | if (t <= 0) t = t + DRP_NUMWDT; /* wrap around? */\r |
| 255 | sim_activate (&drp_unit, t); /* start operation */\r |
| 256 | iosta = iosta | IOS_DRP; /* set busy */\r |
| 257 | break;\r |
| 258 | \r |
| 259 | case 064: /* not assigned */\r |
| 260 | return (stop_inst << IOT_V_REASON) | dat; /* stop if requested */\r |
| 261 | } /* end case */\r |
| 262 | \r |
| 263 | return dat;\r |
| 264 | } /* end if parallel */\r |
| 265 | \r |
| 266 | return (stop_inst << IOT_V_REASON) | dat; /* stop if requested */\r |
| 267 | }\r |
| 268 | \r |
| 269 | /* Serial unit service - this code assumes the entire drum is buffered */\r |
| 270 | \r |
| 271 | t_stat drm_svc (UNIT *uptr)\r |
| 272 | {\r |
| 273 | uint32 i, da;\r |
| 274 | uint32 *fbuf = uptr->filebuf;\r |
| 275 | \r |
| 276 | if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */\r |
| 277 | drm_err = 1; /* set error */\r |
| 278 | iosta = iosta | IOS_DRM; /* set done */\r |
| 279 | dev_req_int (drm_sbs); /* req intr */\r |
| 280 | return IORETURN (drm_stopioe, SCPE_UNATT);\r |
| 281 | }\r |
| 282 | \r |
| 283 | da = drm_da * DRM_NUMWDS; /* compute dev addr */\r |
| 284 | for (i = 0; i < DRM_NUMWDS; i++, da++) { /* do transfer */\r |
| 285 | if (uptr->FUNC == DRM_READ) { /* read? */\r |
| 286 | if (MEM_ADDR_OK (drm_ma)) /* if !nxm */\r |
| 287 | M[drm_ma] = fbuf[da]; /* read word */\r |
| 288 | }\r |
| 289 | else { /* write */\r |
| 290 | if ((drm_wlk >> (drm_da >> 4)) & 1) drm_err = 1;\r |
| 291 | else { /* not locked */\r |
| 292 | fbuf[da] = M[drm_ma]; /* write word */\r |
| 293 | if (da >= uptr->hwmark) uptr->hwmark = da + 1;\r |
| 294 | }\r |
| 295 | }\r |
| 296 | drm_ma = (drm_ma + 1) & AMASK; /* incr mem addr */\r |
| 297 | }\r |
| 298 | drm_da = (drm_da + 1) & DRM_SMASK; /* incr dev addr */\r |
| 299 | iosta = iosta | IOS_DRM; /* set done */\r |
| 300 | dev_req_int (drm_sbs); /* req intr */\r |
| 301 | return SCPE_OK;\r |
| 302 | }\r |
| 303 | \r |
| 304 | /* Reset routine */\r |
| 305 | \r |
| 306 | t_stat drm_reset (DEVICE *dptr)\r |
| 307 | {\r |
| 308 | if ((drm_dev.flags & DEV_DIS) == 0)\r |
| 309 | drp_dev.flags = drp_dev.flags | DEV_DIS;\r |
| 310 | drm_da = drm_ma = drm_err = 0;\r |
| 311 | iosta = iosta & ~IOS_DRM;\r |
| 312 | sim_cancel (&drm_unit);\r |
| 313 | drm_unit.FUNC = 0;\r |
| 314 | return SCPE_OK;\r |
| 315 | }\r |
| 316 | \r |
| 317 | /* Parallel unit service - this code assumes the entire drum is buffered */\r |
| 318 | \r |
| 319 | t_stat drp_svc (UNIT *uptr)\r |
| 320 | {\r |
| 321 | uint32 i, lim;\r |
| 322 | uint32 *fbuf = uptr->filebuf;\r |
| 323 | \r |
| 324 | if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */\r |
| 325 | drp_err = 1; /* set error */\r |
| 326 | iosta = iosta & ~IOS_DRP; /* clear busy */\r |
| 327 | if (uptr->FUNC) dev_req_int (drm_sbs); /* req intr */\r |
| 328 | return IORETURN (drp_stopioe, SCPE_UNATT);\r |
| 329 | }\r |
| 330 | \r |
| 331 | if (uptr->FUNC == DRP_RW) { /* read/write? */\r |
| 332 | lim = drp_wc? drp_wc: DRP_TAMASK + 1; /* eff word count */\r |
| 333 | for (i = 0; i < lim; i++) { /* do transfer */\r |
| 334 | if (drp_wre) /* write enabled? */\r |
| 335 | fbuf[(drp_wrf << DRP_V_FLD) | drp_ta] = M[drp_ma];\r |
| 336 | if (drp_rde && MEM_ADDR_OK (drp_ma)) /* read enabled? */\r |
| 337 | M[drp_ma] = fbuf[(drp_rdf << DRP_V_FLD) | drp_ta];\r |
| 338 | drp_ta = (drp_ta + 1) & DRP_TAMASK; /* incr track addr */\r |
| 339 | drp_ma = ((drp_ma & ~DRP_MAINCM) | ((drp_ma + 1) & DRP_MAINCM));\r |
| 340 | } /* end for */\r |
| 341 | } /* end if */\r |
| 342 | iosta = iosta & ~IOS_DRP; /* clear busy */\r |
| 343 | if (uptr->FUNC) dev_req_int (drm_sbs); /* req intr */\r |
| 344 | return SCPE_OK;\r |
| 345 | }\r |
| 346 | \r |
| 347 | /* Reset routine */\r |
| 348 | \r |
| 349 | t_stat drp_reset (DEVICE *dptr)\r |
| 350 | {\r |
| 351 | if ((drp_dev.flags & DEV_DIS) == 0)\r |
| 352 | drm_dev.flags = drm_dev.flags | DEV_DIS;\r |
| 353 | drp_ta = 0;\r |
| 354 | drp_rde = drp_rdf = drp_wre = drp_wrf = 0;\r |
| 355 | drp_err = 0;\r |
| 356 | drp_ma = 0;\r |
| 357 | drp_wc = 0;\r |
| 358 | iosta = iosta & ~IOS_DRP;\r |
| 359 | sim_cancel (&drp_unit);\r |
| 360 | drp_unit.FUNC = 0;\r |
| 361 | return SCPE_OK;\r |
| 362 | }\r |