1 /* pdp11_io_lib.c: Unibus/Qbus common support routines
2
3 Copyright (c) 1993-2008, 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
27 #if defined (VM_PDP10) /* PDP10 version */
28 #include "pdp10_defs.h"
29
30 #elif defined (VM_VAX) /* VAX version */
31 #include "vax_defs.h"
32
33 #else /* PDP-11 version */
34 #include "pdp11_defs.h"
35 #endif
36 #include "sim_sock.h"
37 #include "sim_tmxr.h"
38
39 extern FILE *sim_log;
40 extern DEVICE *sim_devices[];
41 extern int32 autcon_enb;
42 extern int32 int_vec[IPL_HLVL][32];
43 extern int32 (*int_ack[IPL_HLVL][32])(void);
44 extern t_stat (*iodispR[IOPAGESIZE >> 1])(int32 *dat, int32 ad, int32 md);
45 extern t_stat (*iodispW[IOPAGESIZE >> 1])(int32 dat, int32 ad, int32 md);
46
47 extern t_stat build_dib_tab (void);
48
49 static DIB *iodibp[IOPAGESIZE >> 1];
50
51 /* Enable/disable autoconfiguration */
52
set_autocon(UNIT * uptr,int32 val,char * cptr,void * desc)53 t_stat set_autocon (UNIT *uptr, int32 val, char *cptr, void *desc)
54 {
55 if (cptr != NULL)
56 return SCPE_ARG;
57 autcon_enb = val;
58 return auto_config (NULL, 0);
59 }
60
61 /* Show autoconfiguration status */
62
show_autocon(FILE * st,UNIT * uptr,int32 val,void * desc)63 t_stat show_autocon (FILE *st, UNIT *uptr, int32 val, void *desc)
64 {
65 fprintf (st, "autoconfiguration ");
66 fprintf (st, autcon_enb? "enabled": "disabled");
67 return SCPE_OK;
68 }
69
70 /* Change device address */
71
set_addr(UNIT * uptr,int32 val,char * cptr,void * desc)72 t_stat set_addr (UNIT *uptr, int32 val, char *cptr, void *desc)
73 {
74 DEVICE *dptr;
75 DIB *dibp;
76 uint32 newba;
77 t_stat r;
78
79 if (cptr == NULL)
80 return SCPE_ARG;
81 if ((val == 0) || (uptr == NULL))
82 return SCPE_IERR;
83 dptr = find_dev_from_unit (uptr);
84 if (dptr == NULL)
85 return SCPE_IERR;
86 dibp = (DIB *) dptr->ctxt;
87 if (dibp == NULL)
88 return SCPE_IERR;
89 newba = (uint32) get_uint (cptr, DEV_RDX, IOPAGEBASE+IOPAGEMASK, &r); /* get new */
90 if (r != SCPE_OK)
91 return r;
92 if ((newba <= IOPAGEBASE) || /* > IO page base? */
93 (newba % ((uint32) val))) /* check modulus */
94 return SCPE_ARG;
95 dibp->ba = newba; /* store */
96 dptr->flags = dptr->flags & ~DEV_FLTA; /* not floating */
97 autcon_enb = 0; /* autoconfig off */
98 return SCPE_OK;
99 }
100
101 /* Show device address */
102
show_addr(FILE * st,UNIT * uptr,int32 val,void * desc)103 t_stat show_addr (FILE *st, UNIT *uptr, int32 val, void *desc)
104 {
105 DEVICE *dptr;
106 DIB *dibp;
107
108 if (uptr == NULL)
109 return SCPE_IERR;
110 dptr = find_dev_from_unit (uptr);
111 if (dptr == NULL)
112 return SCPE_IERR;
113 dibp = (DIB *) dptr->ctxt;
114 if ((dibp == NULL) || (dibp->ba <= IOPAGEBASE))
115 return SCPE_IERR;
116 fprintf (st, "address=");
117 fprint_val (st, (t_value) dibp->ba, DEV_RDX, 32, PV_LEFT);
118 if (dibp->lnt > 1) {
119 fprintf (st, "-");
120 fprint_val (st, (t_value) dibp->ba + dibp->lnt - 1, DEV_RDX, 32, PV_LEFT);
121 }
122 if (dptr->flags & DEV_FLTA)
123 fprintf (st, "*");
124 return SCPE_OK;
125 }
126
127 /* Set address floating */
128
set_addr_flt(UNIT * uptr,int32 val,char * cptr,void * desc)129 t_stat set_addr_flt (UNIT *uptr, int32 val, char *cptr, void *desc)
130 {
131 DEVICE *dptr;
132
133 if (cptr != NULL)
134 return SCPE_ARG;
135 if (uptr == NULL)
136 return SCPE_IERR;
137 dptr = find_dev_from_unit (uptr);
138 if (dptr == NULL)
139 return SCPE_IERR;
140 dptr->flags = dptr->flags | DEV_FLTA; /* floating */
141 return auto_config (NULL, 0); /* autoconfigure */
142 }
143
144 /* Change device vector */
145
set_vec(UNIT * uptr,int32 arg,char * cptr,void * desc)146 t_stat set_vec (UNIT *uptr, int32 arg, char *cptr, void *desc)
147 {
148 DEVICE *dptr;
149 DIB *dibp;
150 uint32 newvec;
151 t_stat r;
152
153 if (cptr == NULL)
154 return SCPE_ARG;
155 if (uptr == NULL)
156 return SCPE_IERR;
157 dptr = find_dev_from_unit (uptr);
158 if (dptr == NULL)
159 return SCPE_IERR;
160 dibp = (DIB *) dptr->ctxt;
161 if (dibp == NULL)
162 return SCPE_IERR;
163 newvec = (uint32) get_uint (cptr, DEV_RDX, VEC_Q + 01000, &r);
164 if ((r != SCPE_OK) || (newvec == VEC_Q) ||
165 ((newvec + (dibp->vnum * 4)) >= (VEC_Q + 01000)) ||
166 (newvec & ((dibp->vnum > 1)? 07: 03)))
167 return SCPE_ARG;
168 dibp->vec = newvec;
169 dptr->flags = dptr->flags & ~DEV_FLTA; /* not floating */
170 autcon_enb = 0; /* autoconfig off */
171 return SCPE_OK;
172 }
173
174 /* Show device vector */
175
show_vec(FILE * st,UNIT * uptr,int32 arg,void * desc)176 t_stat show_vec (FILE *st, UNIT *uptr, int32 arg, void *desc)
177 {
178 DEVICE *dptr;
179 DIB *dibp;
180 uint32 vec, numvec;
181
182 if (uptr == NULL)
183 return SCPE_IERR;
184 dptr = find_dev_from_unit (uptr);
185 if (dptr == NULL)
186 return SCPE_IERR;
187 dibp = (DIB *) dptr->ctxt;
188 if (dibp == NULL)
189 return SCPE_IERR;
190 vec = dibp->vec;
191 if (arg)
192 numvec = arg;
193 else numvec = dibp->vnum;
194 if (vec == 0)
195 fprintf (st, "no vector");
196 else {
197 fprintf (st, "vector=");
198 fprint_val (st, (t_value) vec, DEV_RDX, 16, PV_LEFT);
199 if (numvec > 1) {
200 fprintf (st, "-");
201 fprint_val (st, (t_value) vec + (4 * (numvec - 1)), DEV_RDX, 16, PV_LEFT);
202 }
203 }
204 return SCPE_OK;
205 }
206
207 /* Show vector for terminal multiplexor */
208
show_vec_mux(FILE * st,UNIT * uptr,int32 arg,void * desc)209 t_stat show_vec_mux (FILE *st, UNIT *uptr, int32 arg, void *desc)
210 {
211 TMXR *mp = (TMXR *) desc;
212
213 if ((mp == NULL) || (arg == 0))
214 return SCPE_IERR;
215 return show_vec (st, uptr, ((mp->lines * 2) / arg), desc);
216 }
217
218 /* Init Unibus tables */
219
init_ubus_tab(void)220 void init_ubus_tab (void)
221 {
222 int32 i, j;
223
224 for (i = 0; i < IPL_HLVL; i++) { /* clear intr tab */
225 for (j = 0; j < 32; j++) {
226 int_vec[i][j] = 0;
227 int_ack[i][j] = NULL;
228 }
229 }
230 for (i = 0; i < (IOPAGESIZE >> 1); i++) { /* clear dispatch tab */
231 iodispR[i] = NULL;
232 iodispW[i] = NULL;
233 iodibp[i] = NULL;
234 }
235 return;
236 }
237
238 /* Build Unibus tables */
239
build_ubus_tab(DEVICE * dptr,DIB * dibp)240 t_stat build_ubus_tab (DEVICE *dptr, DIB *dibp)
241 {
242 int32 i, idx, vec, ilvl, ibit;
243
244 if ((dptr == NULL) || (dibp == NULL)) /* validate args */
245 return SCPE_IERR;
246 if (dibp->vnum > VEC_DEVMAX)
247 return SCPE_IERR;
248 for (i = 0; i < dibp->vnum; i++) { /* loop thru vec */
249 idx = dibp->vloc + i; /* vector index */
250 vec = dibp->vec? (dibp->vec + (i * 4)): 0; /* vector addr */
251 ilvl = idx / 32;
252 ibit = idx % 32;
253 if ((int_ack[ilvl][ibit] && dibp->ack[i] && /* conflict? */
254 (int_ack[ilvl][ibit] != dibp->ack[i])) ||
255 (int_vec[ilvl][ibit] && vec &&
256 (int_vec[ilvl][ibit] != vec))) {
257 printf ("Device %s interrupt slot conflict at %d\n",
258 sim_dname (dptr), idx);
259 if (sim_log)
260 fprintf (sim_log, "Device %s interrupt slot conflict at %d\n",
261 sim_dname (dptr), idx);
262 return SCPE_STOP;
263 }
264 if (dibp->ack[i])
265 int_ack[ilvl][ibit] = dibp->ack[i];
266 else if (vec)
267 int_vec[ilvl][ibit] = vec;
268 }
269 for (i = 0; i < (int32) dibp->lnt; i = i + 2) { /* create entries */
270 idx = ((dibp->ba + i) & IOPAGEMASK) >> 1; /* index into disp */
271 if ((iodispR[idx] && dibp->rd && /* conflict? */
272 (iodispR[idx] != dibp->rd)) ||
273 (iodispW[idx] && dibp->wr &&
274 (iodispW[idx] != dibp->wr))) {
275 printf ("Device %s address conflict at \n", sim_dname (dptr));
276 fprint_val (stdout, (t_value) dibp->ba, DEV_RDX, 32, PV_LEFT);
277 if (sim_log) {
278 fprintf (sim_log, "Device %s address conflict at \n", sim_dname (dptr));
279 fprint_val (sim_log, (t_value) dibp->ba, DEV_RDX, 32, PV_LEFT);
280 }
281 return SCPE_STOP;
282 }
283 if (dibp->rd) /* set rd dispatch */
284 iodispR[idx] = dibp->rd;
285 if (dibp->wr) /* set wr dispatch */
286 iodispW[idx] = dibp->wr;
287 iodibp[idx] = dibp; /* remember DIB */
288 }
289 return SCPE_OK;
290 }
291
292 /* Show IO space */
293
show_iospace(FILE * st,UNIT * uptr,int32 val,void * desc)294 t_stat show_iospace (FILE *st, UNIT *uptr, int32 val, void *desc)
295 {
296 uint32 i, j;
297 DEVICE *dptr;
298 DIB *dibp;
299
300 if (build_dib_tab ()) /* build IO page */
301 return SCPE_OK;
302 for (i = 0, dibp = NULL; i < (IOPAGESIZE >> 1); i++) { /* loop thru entries */
303 if (iodibp[i] && (iodibp[i] != dibp)) { /* new block? */
304 dibp = iodibp[i]; /* DIB for block */
305 for (j = 0, dptr = NULL; sim_devices[j] != NULL; j++) {
306 if (((DIB*) sim_devices[j]->ctxt) == dibp) {
307 dptr = sim_devices[j]; /* locate device */
308 break;
309 } /* end if */
310 } /* end for j */
311 fprint_val (st, (t_value) dibp->ba, DEV_RDX, 32, PV_LEFT);
312 fprintf (st, " - ");
313 fprint_val (st, (t_value) dibp->ba + dibp->lnt - 1, DEV_RDX, 32, PV_LEFT);
314 fprintf (st, "%c\t%s\n", /* print block entry */
315 (dptr && (dptr->flags & DEV_FLTA))? '*': ' ',
316 dptr? sim_dname (dptr): "CPU");
317 } /* end if */
318 } /* end for i */
319 return SCPE_OK;
320 }
321
322 /* Autoconfiguration
323
324 The table reflects the MicroVAX 3900 microcode, with one addition - the
325 number of controllers field handles devices where multiple instances
326 are simulated through a single DEVICE structure (e.g., DZ, VH).
327
328 A minus number of vectors indicates a field that should be calculated
329 but not placed in the DIB (RQ, TQ dynamic vectors) */
330
331 #define AUTO_MAXC 4
332 #define AUTO_CSRBASE 0010
333 #define AUTO_VECBASE 0300
334
335 typedef struct {
336 char *dnam[AUTO_MAXC];
337 int32 numc;
338 int32 numv;
339 uint32 amod;
340 uint32 vmod;
341 uint32 fixa[AUTO_MAXC];
342 uint32 fixv[AUTO_MAXC];
343 } AUTO_CON;
344
345 AUTO_CON auto_tab[] = {
346 { { "DCI" }, DCX_LINES, 2, 0, 8, { 0 } }, /* DC11 - fx CSRs */
347 { { "DLI" }, DLX_LINES, 2, 0, 8, { 0 } }, /* KL11/DL11/DLV11 - fx CSRs */
348 { { NULL }, 1, 2, 0, 8, { 0 } }, /* DLV11J - fx CSRs */
349 { { NULL }, 1, 2, 8, 8 }, /* DJ11 */
350 { { NULL }, 1, 2, 16, 8 }, /* DH11 */
351 { { NULL }, 1, 2, 8, 8 }, /* DQ11 */
352 { { NULL }, 1, 2, 8, 8 }, /* DU11 */
353 { { NULL }, 1, 2, 8, 8 }, /* DUP11 */
354 { { NULL }, 10, 2, 8, 8 }, /* LK11A */
355 { { NULL }, 1, 2, 8, 8 }, /* DMC11 */
356 { { "DZ" }, DZ_MUXES, 2, 8, 8 }, /* DZ11 */
357 { { NULL }, 1, 2, 8, 8 }, /* KMC11 */
358 { { NULL }, 1, 2, 8, 8 }, /* LPP11 */
359 { { NULL }, 1, 2, 8, 8 }, /* VMV21 */
360 { { NULL }, 1, 2, 16, 8 }, /* VMV31 */
361 { { NULL }, 1, 2, 8, 8 }, /* DWR70 */
362 { { "RL", "RLB" }, 1, 1, 8, 4, {IOBA_RL}, {VEC_RL} }, /* RL11 */
363 { { "TS", "TSB", "TSC", "TSD" }, 1, 1, 0, 4, /* TS11 */
364 {IOBA_TS, IOBA_TS + 4, IOBA_TS + 8, IOBA_TS + 12},
365 {VEC_TS} },
366 { { NULL }, 1, 2, 16, 8 }, /* LPA11K */
367 { { NULL }, 1, 2, 8, 8 }, /* KW11C */
368 { { NULL }, 1, 1, 8, 8 }, /* reserved */
369 { { "RX", "RY" }, 1, 1, 8, 4, {IOBA_RX} , {VEC_RX} }, /* RX11/RX211 */
370 { { NULL }, 1, 1, 8, 4 }, /* DR11W */
371 { { NULL }, 1, 1, 8, 4, { 0, 0 }, { 0 } }, /* DR11B - fx CSRs,vec */
372 { { NULL }, 1, 2, 8, 8 }, /* DMP11 */
373 { { NULL }, 1, 2, 8, 8 }, /* DPV11 */
374 { { NULL }, 1, 2, 8, 8 }, /* ISB11 */
375 { { NULL }, 1, 2, 16, 8 }, /* DMV11 */
376 { { "XU", "XUB" }, 1, 1, 8, 4, {IOBA_XU}, {VEC_XU} }, /* DEUNA */
377 { { "XQ", "XQB" }, 1, 1, 0, 4, /* DEQNA */
378 {IOBA_XQ,IOBA_XQB}, {VEC_XQ} },
379 { { "RQ", "RQB", "RQC", "RQD" }, 1, -1, 4, 4, /* RQDX3 */
380 {IOBA_RQ}, {VEC_RQ} },
381 { { NULL }, 1, 8, 32, 4 }, /* DMF32 */
382 { { NULL }, 1, 2, 16, 8 }, /* KMS11 */
383 { { NULL }, 1, 1, 16, 4 }, /* VS100 */
384 { { "TQ", "TQB" }, 1, -1, 4, 4, {IOBA_TQ}, {VEC_TQ} }, /* TQK50 */
385 { { NULL }, 1, 2, 16, 8 }, /* KMV11 */
386 { { "VH" }, VH_MUXES, 2, 16, 8 }, /* DHU11/DHQ11 */
387 { { NULL }, 1, 6, 32, 4 }, /* DMZ32 */
388 { { NULL }, 1, 6, 32, 4 }, /* CP132 */
389 { { NULL }, 1, 2, 64, 8, { 0 } }, /* QVSS - fx CSR */
390 { { NULL }, 1, 1, 8, 4 }, /* VS31 */
391 { { NULL }, 1, 1, 0, 4, { 0 } }, /* LNV11 - fx CSR */
392 { { NULL }, 1, 1, 16, 4 }, /* LNV21/QPSS */
393 { { NULL }, 1, 1, 8, 4, { 0 } }, /* QTA - fx CSR */
394 { { NULL }, 1, 1, 8, 4 }, /* DSV11 */
395 { { NULL }, 1, 2, 8, 8 }, /* CSAM */
396 { { NULL }, 1, 2, 8, 8 }, /* ADV11C */
397 { { NULL }, 1, 0, 8, 0 }, /* AAV11C */
398 { { NULL }, 1, 2, 8, 8, { 0 }, { 0 } }, /* AXV11C - fx CSR,vec */
399 { { NULL }, 1, 2, 4, 8, { 0 } }, /* KWV11C - fx CSR */
400 { { NULL }, 1, 2, 8, 8, { 0 } }, /* ADV11D - fx CSR */
401 { { NULL }, 1, 2, 8, 8, { 0 } }, /* AAV11D - fx CSR */
402 { { "QDSS" }, 1, 3, 0, 16, {IOBA_QDSS} }, /* QDSS - fx CSR */
403 { { NULL }, -1 } /* end table */
404 };
405
auto_config(char * name,int32 nctrl)406 t_stat auto_config (char *name, int32 nctrl)
407 {
408 uint32 csr = IOPAGEBASE + AUTO_CSRBASE;
409 uint32 vec = VEC_Q + AUTO_VECBASE;
410 AUTO_CON *autp;
411 DEVICE *dptr;
412 DIB *dibp;
413 uint32 j, k, vmask, amask;
414
415 if (autcon_enb == 0) /* enabled? */
416 return SCPE_OK;
417 if (name) { /* updating? */
418 if (nctrl < 0)
419 return SCPE_ARG;
420 for (autp = auto_tab; autp->numc >= 0; autp++) {
421 for (j = 0; (j < AUTO_MAXC) && autp->dnam[j]; j++) {
422 if (strcmp (name, autp->dnam[j]) == 0)
423 autp->numc = nctrl;
424 }
425 }
426 }
427 for (autp = auto_tab; autp->numc >= 0; autp++) { /* loop thru table */
428 if (autp->amod) { /* floating csr? */
429 amask = autp->amod - 1;
430 csr = (csr + amask) & ~amask; /* align csr */
431 }
432 for (j = k = 0; (j < AUTO_MAXC) && autp->dnam[j]; j++) {
433 if (autp->dnam[j] == NULL) /* no device? */
434 continue;
435 dptr = find_dev (autp->dnam[j]); /* find ctrl */
436 if ((dptr == NULL) || /* enabled, floating? */
437 (dptr->flags & DEV_DIS) ||
438 !(dptr->flags & DEV_FLTA))
439 continue;
440 dibp = (DIB *) dptr->ctxt; /* get DIB */
441 if (dibp == NULL) /* not there??? */
442 return SCPE_IERR;
443 if (autp->amod) { /* dyn csr needed? */
444 if (autp->fixa[k]) /* fixed csr avail? */
445 dibp->ba = autp->fixa[k]; /* use it */
446 else { /* no fixed left */
447 dibp->ba = csr; /* set CSR */
448 csr += (autp->numc * autp->amod); /* next CSR */
449 } /* end else */
450 } /* end if dyn csr */
451 if (autp->numv && autp->vmod) { /* dyn vec needed? */
452 uint32 numv = abs (autp->numv); /* get num vec */
453 if (autp->fixv[k]) { /* fixed vec avail? */
454 if (autp->numv > 0)
455 dibp->vec = autp->fixv[k]; /* use it */
456 }
457 else { /* no fixed left */
458 vmask = autp->vmod - 1;
459 vec = (vec + vmask) & ~vmask; /* align vector */
460 if (autp->numv > 0)
461 dibp->vec = vec; /* set vector */
462 vec += (autp->numc * numv * 4);
463 } /* end else */
464 } /* end if dyn vec */
465 k++; /* next instance */
466 } /* end for j */
467 if (autp->amod) /* flt CSR? gap */
468 csr = csr + 2;
469 } /* end for i */
470 return SCPE_OK;
471 }
472
473 /* Factory bad block table creation routine
474
475 This routine writes a DEC standard 044 compliant bad block table on the
476 last track of the specified unit. The bad block table consists of 10
477 repetitions of the same table, formatted as follows:
478
479 words 0-1 pack id number
480 words 2-3 cylinder/sector/surface specifications
481 :
482 words n-n+1 end of table (-1,-1)
483
484 Inputs:
485 uptr = pointer to unit
486 sec = number of sectors per surface
487 wds = number of words per sector
488 Outputs:
489 sta = status code
490 */
491
pdp11_bad_block(UNIT * uptr,int32 sec,int32 wds)492 t_stat pdp11_bad_block (UNIT *uptr, int32 sec, int32 wds)
493 {
494 int32 i;
495 t_addr da;
496 uint16 *buf;
497
498 if ((sec < 2) || (wds < 16))
499 return SCPE_ARG;
500 if ((uptr->flags & UNIT_ATT) == 0)
501 return SCPE_UNATT;
502 if (uptr->flags & UNIT_RO)
503 return SCPE_RO;
504 if (!get_yn ("Overwrite last track? [N]", FALSE))
505 return SCPE_OK;
506 da = (uptr->capac - (sec * wds)) * sizeof (uint16);
507 if (sim_fseek (uptr->fileref, da, SEEK_SET))
508 return SCPE_IOERR;
509 if ((buf = (uint16 *) malloc (wds * sizeof (uint16))) == NULL)
510 return SCPE_MEM;
511 buf[0] = buf[1] = 012345u;
512 buf[2] = buf[3] = 0;
513 for (i = 4; i < wds; i++)
514 buf[i] = 0177777u;
515 for (i = 0; (i < sec) && (i < 10); i++)
516 sim_fwrite (buf, sizeof (uint16), wds, uptr->fileref);
517 free (buf);
518 if (ferror (uptr->fileref))
519 return SCPE_IOERR;
520 return SCPE_OK;
521 }
522