First Commit of my working state
[simh.git] / PDP11 / pdp11_io.c
1 /* pdp11_io.c: PDP-11 I/O simulator
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 16-May-08 RMS Added multiple DC11 support
27 Renamed DL11 in autoconfigure
28 02-Feb-08 RMS Fixed DMA memory address limit test (found by John Dundas)
29 06-Jul-06 RMS Added multiple KL11/DL11 support
30 15-Oct-05 RMS Fixed bug in autoconfiguration (missing XU)
31 25-Jul-05 RMS Revised autoconfiguration algorithm and interface
32 30-Sep-04 RMS Revised Unibus interface
33 28-May-04 RMS Revised I/O dispatching (from John Dundas)
34 25-Jan-04 RMS Removed local debug logging support
35 21-Dec-03 RMS Fixed bug in autoconfigure vector assignment; added controls
36 21-Nov-03 RMS Added check for interrupt slot conflict (found by Dave Hittner)
37 12-Mar-03 RMS Added logical name support
38 08-Oct-02 RMS Trimmed I/O bus addresses
39 Added support for dynamic tables
40 Added show I/O space, autoconfigure routines
41 12-Sep-02 RMS Added support for TMSCP, KW11P, RX211
42 26-Jan-02 RMS Revised for multiple DZ's
43 06-Jan-02 RMS Revised I/O access, enable/disable support
44 11-Dec-01 RMS Moved interrupt debug code
45 08-Nov-01 RMS Cloned from cpu sources
46 */
47
48 #include "pdp11_defs.h"
49
50 extern uint16 *M;
51 extern int32 int_req[IPL_HLVL];
52 extern int32 ub_map[UBM_LNT_LW];
53 extern int32 cpu_opt, cpu_bme;
54 extern int32 trap_req, ipl;
55 extern int32 cpu_log;
56 extern int32 autcon_enb;
57 extern int32 uba_last;
58 extern FILE *sim_log;
59 extern DEVICE *sim_devices[], cpu_dev;
60 extern t_addr cpu_memsize;
61
62 int32 calc_ints (int32 nipl, int32 trq);
63
64 extern t_stat cpu_build_dib (void);
65 extern void init_mbus_tab (void);
66 extern t_stat build_mbus_tab (DEVICE *dptr, DIB *dibp);
67
68 /* I/O data structures */
69
70 static t_stat (*iodispR[IOPAGESIZE >> 1])(int32 *dat, int32 ad, int32 md);
71 static t_stat (*iodispW[IOPAGESIZE >> 1])(int32 dat, int32 ad, int32 md);
72 static DIB *iodibp[IOPAGESIZE >> 1];
73
74 int32 int_vec[IPL_HLVL][32]; /* int req to vector */
75
76 int32 (*int_ack[IPL_HLVL][32])(void); /* int ack routines */
77
78 static const int32 pirq_bit[7] = {
79 INT_V_PIR1, INT_V_PIR2, INT_V_PIR3, INT_V_PIR4,
80 INT_V_PIR5, INT_V_PIR6, INT_V_PIR7
81 };
82
83 /* I/O page lookup and linkage routines
84
85 Inputs:
86 *data = pointer to data to read, if READ
87 data = data to store, if WRITE or WRITEB
88 pa = address
89 access = READ, WRITE, or WRITEB
90 Outputs:
91 status = SCPE_OK or SCPE_NXM
92 */
93
94 t_stat iopageR (int32 *data, uint32 pa, int32 access)
95 {
96 int32 idx;
97 t_stat stat;
98
99 idx = (pa & IOPAGEMASK) >> 1;
100 if (iodispR[idx]) {
101 stat = iodispR[idx] (data, pa, access);
102 trap_req = calc_ints (ipl, trap_req);
103 return stat;
104 }
105 return SCPE_NXM;
106 }
107
108 t_stat iopageW (int32 data, uint32 pa, int32 access)
109 {
110 int32 idx;
111 t_stat stat;
112
113 idx = (pa & IOPAGEMASK) >> 1;
114 if (iodispW[idx]) {
115 stat = iodispW[idx] (data, pa, access);
116 trap_req = calc_ints (ipl, trap_req);
117 return stat;
118 }
119 return SCPE_NXM;
120 }
121
122 /* Calculate interrupt outstanding */
123
124 int32 calc_ints (int32 nipl, int32 trq)
125 {
126 int32 i;
127
128 for (i = IPL_HLVL - 1; i > nipl; i--) {
129 if (int_req[i]) return (trq | TRAP_INT);
130 }
131 return (trq & ~TRAP_INT);
132 }
133
134 /* Find vector for highest priority interrupt */
135
136 int32 get_vector (int32 nipl)
137 {
138 int32 i, j, t, vec;
139
140 for (i = IPL_HLVL - 1; i > nipl; i--) { /* loop thru lvls */
141 t = int_req[i]; /* get level */
142 for (j = 0; t && (j < 32); j++) { /* srch level */
143 if ((t >> j) & 1) { /* irq found? */
144 int_req[i] = int_req[i] & ~(1u << j); /* clr irq */
145 if (int_ack[i][j]) vec = int_ack[i][j]();
146 else vec = int_vec[i][j];
147 return vec; /* return vector */
148 } /* end if t */
149 } /* end for j */
150 } /* end for i */
151 return 0;
152 }
153
154 /* Read and write Unibus map registers
155
156 In any even/odd pair
157 even = low 16b, bit <0> clear
158 odd = high 6b
159
160 The Unibus map is stored as an array of longwords.
161 These routines are only reachable if a Unibus map is configured.
162 */
163
164 t_stat ubm_rd (int32 *data, int32 addr, int32 access)
165 {
166 int32 pg = (addr >> 2) & UBM_M_PN;
167
168 *data = (addr & 2)? ((ub_map[pg] >> 16) & 077):
169 (ub_map[pg] & 0177776);
170 return SCPE_OK;
171 }
172
173 t_stat ubm_wr (int32 data, int32 addr, int32 access)
174 {
175 int32 sc, pg = (addr >> 2) & UBM_M_PN;
176
177 if (access == WRITEB) {
178 sc = (addr & 3) << 3;
179 ub_map[pg] = (ub_map[pg] & ~(0377 << sc)) |
180 ((data & 0377) << sc);
181 }
182 else {
183 sc = (addr & 2) << 3;
184 ub_map[pg] = (ub_map[pg] & ~(0177777 << sc)) |
185 ((data & 0177777) << sc);
186 }
187 ub_map[pg] = ub_map[pg] & 017777776;
188 return SCPE_OK;
189 }
190
191 /* Mapped memory access routines for DMA devices */
192
193 #define BUSMASK ((UNIBUS)? UNIMASK: PAMASK)
194
195 /* Map I/O address to memory address - caller checks cpu_bme */
196
197 uint32 Map_Addr (uint32 ba)
198 {
199 int32 pg = UBM_GETPN (ba); /* map entry */
200 int32 off = UBM_GETOFF (ba); /* offset */
201
202 if (pg != UBM_M_PN) /* last page? */
203 uba_last = (ub_map[pg] + off) & PAMASK; /* no, use map */
204 else uba_last = (IOPAGEBASE + off) & PAMASK; /* yes, use fixed */
205 return uba_last;
206 }
207
208 /* I/O buffer routines, aligned access
209
210 Map_ReadB - fetch byte buffer from memory
211 Map_ReadW - fetch word buffer from memory
212 Map_WriteB - store byte buffer into memory
213 Map_WriteW - store word buffer into memory
214
215 These routines are used only for Unibus and Qbus devices.
216 Massbus devices have their own IO routines. As a result,
217 the historic 'map' parameter is no longer needed.
218
219 - In a U18 configuration, the map is always disabled.
220 Device addresses are trimmed to 18b.
221 - In a U22 configuration, the map is always configured
222 (although it may be disabled). Device addresses are
223 trimmed to 18b.
224 - In a Qbus configuration, the map is always disabled.
225 Device addresses are trimmed to 22b.
226 */
227
228 int32 Map_ReadB (uint32 ba, int32 bc, uint8 *buf)
229 {
230 uint32 alim, lim, ma;
231
232 ba = ba & BUSMASK; /* trim address */
233 lim = ba + bc;
234 if (cpu_bme) { /* map enabled? */
235 for ( ; ba < lim; ba++) { /* by bytes */
236 ma = Map_Addr (ba); /* map addr */
237 if (!ADDR_IS_MEM (ma)) return (lim - ba); /* NXM? err */
238 if (ma & 1) *buf++ = (M[ma >> 1] >> 8) & 0377; /* get byte */
239 else *buf++ = M[ma >> 1] & 0377;
240 }
241 return 0;
242 }
243 else { /* physical */
244 if (ADDR_IS_MEM (lim)) alim = lim; /* end ok? */
245 else if (ADDR_IS_MEM (ba)) alim = cpu_memsize; /* no, strt ok? */
246 else return bc; /* no, err */
247 for ( ; ba < alim; ba++) { /* by bytes */
248 if (ba & 1) *buf++ = (M[ba >> 1] >> 8) & 0377; /* get byte */
249 else *buf++ = M[ba >> 1] & 0377;
250 }
251 return (lim - alim);
252 }
253 }
254
255 int32 Map_ReadW (uint32 ba, int32 bc, uint16 *buf)
256 {
257 uint32 alim, lim, ma;
258
259 ba = (ba & BUSMASK) & ~01; /* trim, align addr */
260 lim = ba + (bc & ~01);
261 if (cpu_bme) { /* map enabled? */
262 for (; ba < lim; ba = ba + 2) { /* by words */
263 ma = Map_Addr (ba); /* map addr */
264 if (!ADDR_IS_MEM (ma)) return (lim - ba); /* NXM? err */
265 *buf++ = M[ma >> 1];
266 }
267 return 0;
268 }
269 else { /* physical */
270 if (ADDR_IS_MEM (lim)) alim = lim; /* end ok? */
271 else if (ADDR_IS_MEM (ba)) alim = cpu_memsize; /* no, strt ok? */
272 else return bc; /* no, err */
273 for ( ; ba < alim; ba = ba + 2) { /* by words */
274 *buf++ = M[ba >> 1];
275 }
276 return (lim - alim);
277 }
278 }
279
280 int32 Map_WriteB (uint32 ba, int32 bc, uint8 *buf)
281 {
282 uint32 alim, lim, ma;
283
284 ba = ba & BUSMASK; /* trim address */
285 lim = ba + bc;
286 if (cpu_bme) { /* map enabled? */
287 for ( ; ba < lim; ba++) { /* by bytes */
288 ma = Map_Addr (ba); /* map addr */
289 if (!ADDR_IS_MEM (ma)) return (lim - ba); /* NXM? err */
290 if (ma & 1) M[ma >> 1] = (M[ma >> 1] & 0377) |
291 ((uint16) *buf++ << 8);
292 else M[ma >> 1] = (M[ma >> 1] & ~0377) | *buf++;
293 }
294 return 0;
295 }
296 else { /* physical */
297 if (ADDR_IS_MEM (lim)) alim = lim; /* end ok? */
298 else if (ADDR_IS_MEM (ba)) alim = cpu_memsize; /* no, strt ok? */
299 else return bc; /* no, err */
300 for ( ; ba < alim; ba++) { /* by bytes */
301 if (ba & 1) M[ba >> 1] = (M[ba >> 1] & 0377) |
302 ((uint16) *buf++ << 8);
303 else M[ba >> 1] = (M[ba >> 1] & ~0377) | *buf++;
304 }
305 return (lim - alim);
306 }
307 }
308
309 int32 Map_WriteW (uint32 ba, int32 bc, uint16 *buf)
310 {
311 uint32 alim, lim, ma;
312
313 ba = (ba & BUSMASK) & ~01; /* trim, align addr */
314 lim = ba + (bc & ~01);
315 if (cpu_bme) { /* map enabled? */
316 for (; ba < lim; ba = ba + 2) { /* by words */
317 ma = Map_Addr (ba); /* map addr */
318 if (!ADDR_IS_MEM (ma)) return (lim - ba); /* NXM? err */
319 M[ma >> 1] = *buf++;
320 }
321 return 0;
322 }
323 else { /* physical */
324 if (ADDR_IS_MEM (lim)) alim = lim; /* end ok? */
325 else if (ADDR_IS_MEM (ba)) alim = cpu_memsize; /* no, strt ok? */
326 else return bc; /* no, err */
327 for ( ; ba < alim; ba = ba + 2) { /* by words */
328 M[ba >> 1] = *buf++;
329 }
330 return (lim - alim);
331 }
332 }
333
334 /* Enable/disable autoconfiguration */
335
336 t_stat set_autocon (UNIT *uptr, int32 val, char *cptr, void *desc)
337 {
338 if (cptr != NULL) return SCPE_ARG;
339 autcon_enb = val;
340 return auto_config (NULL, 0);
341 }
342
343 /* Show autoconfiguration status */
344
345 t_stat show_autocon (FILE *st, UNIT *uptr, int32 val, void *desc)
346 {
347 fprintf (st, "autoconfiguration %s", (autcon_enb? "on": "off"));
348 return SCPE_OK;
349 }
350
351 /* Change device address */
352
353 t_stat set_addr (UNIT *uptr, int32 val, char *cptr, void *desc)
354 {
355 DEVICE *dptr;
356 DIB *dibp;
357 uint32 newba;
358 t_stat r;
359
360 if (cptr == NULL) return SCPE_ARG;
361 if ((val == 0) || (uptr == NULL)) return SCPE_IERR;
362 dptr = find_dev_from_unit (uptr);
363 if (dptr == NULL) return SCPE_IERR;
364 dibp = (DIB *) dptr->ctxt;
365 if (dibp == NULL) return SCPE_IERR;
366 newba = get_uint (cptr, 8, PAMASK, &r); /* get new */
367 if (r != SCPE_OK) return r; /* error? */
368 if ((newba <= IOPAGEBASE) || /* > IO page base? */
369 (newba % ((uint32) val))) return SCPE_ARG; /* check modulus */
370 dibp->ba = newba; /* store */
371 dptr->flags = dptr->flags & ~DEV_FLTA; /* not floating */
372 autcon_enb = 0; /* autoconfig off */
373 return SCPE_OK;
374 }
375
376 /* Show device address */
377
378 t_stat show_addr (FILE *st, UNIT *uptr, int32 val, void *desc)
379 {
380 DEVICE *dptr;
381 DIB *dibp;
382
383 if (uptr == NULL) return SCPE_IERR;
384 dptr = find_dev_from_unit (uptr);
385 if (dptr == NULL) return SCPE_IERR;
386 dibp = (DIB *) dptr->ctxt;
387 if ((dibp == NULL) || (dibp->ba <= IOPAGEBASE)) return SCPE_IERR;
388 fprintf (st, "address=%08o", dibp->ba);
389 if (dibp->lnt > 1)
390 fprintf (st, "-%08o", dibp->ba + dibp->lnt - 1);
391 if (dptr->flags & DEV_FLTA) fprintf (st, "*");
392 return SCPE_OK;
393 }
394
395 /* Set address floating */
396
397 t_stat set_addr_flt (UNIT *uptr, int32 val, char *cptr, void *desc)
398 {
399 DEVICE *dptr;
400
401 if (cptr != NULL) return SCPE_ARG;
402 if (uptr == NULL) return SCPE_IERR;
403 dptr = find_dev_from_unit (uptr);
404 if (dptr == NULL) return SCPE_IERR;
405 dptr->flags = dptr->flags | DEV_FLTA; /* floating */
406 return auto_config (NULL, 0); /* autoconfigure */
407 }
408
409 /* Change device vector */
410
411 t_stat set_vec (UNIT *uptr, int32 arg, char *cptr, void *desc)
412 {
413 DEVICE *dptr;
414 DIB *dibp;
415 uint32 newvec;
416 t_stat r;
417
418 if (cptr == NULL) return SCPE_ARG;
419 if (uptr == NULL) return SCPE_IERR;
420 dptr = find_dev_from_unit (uptr);
421 if (dptr == NULL) return SCPE_IERR;
422 dibp = (DIB *) dptr->ctxt;
423 if (dibp == NULL) return SCPE_IERR;
424 newvec = get_uint (cptr, 8, VEC_Q + 01000, &r);
425 if ((r != SCPE_OK) || (newvec == VEC_Q) ||
426 ((newvec + (dibp->vnum * 4)) >= (VEC_Q + 01000)) ||
427 (newvec & ((dibp->vnum > 1)? 07: 03))) return SCPE_ARG;
428 dibp->vec = newvec;
429 dptr->flags = dptr->flags & ~DEV_FLTA; /* not floating */
430 autcon_enb = 0; /* autoconfig off */
431 return SCPE_OK;
432 }
433
434 /* Show device vector */
435
436 t_stat show_vec (FILE *st, UNIT *uptr, int32 arg, void *desc)
437 {
438 DEVICE *dptr;
439 DIB *dibp;
440 uint32 vec, numvec;
441
442 if (uptr == NULL) return SCPE_IERR;
443 dptr = find_dev_from_unit (uptr);
444 if (dptr == NULL) return SCPE_IERR;
445 dibp = (DIB *) dptr->ctxt;
446 if (dibp == NULL) return SCPE_IERR;
447 vec = dibp->vec;
448 if (arg) numvec = arg;
449 else numvec = dibp->vnum;
450 if (vec == 0) fprintf (st, "no vector");
451 else {
452 fprintf (st, "vector=%o", vec);
453 if (numvec > 1) fprintf (st, "-%o", vec + (4 * (numvec - 1)));
454 }
455 return SCPE_OK;
456 }
457
458 /* Init Unibus tables */
459
460 void init_ubus_tab (void)
461 {
462 int32 i, j;
463
464 for (i = 0; i < IPL_HLVL; i++) { /* clear intr tab */
465 for (j = 0; j < 32; j++) {
466 int_vec[i][j] = 0;
467 int_ack[i][j] = NULL;
468 }
469 }
470 for (i = 0; i < (IOPAGESIZE >> 1); i++) { /* clear dispatch tab */
471 iodispR[i] = NULL;
472 iodispW[i] = NULL;
473 iodibp[i] = NULL;
474 }
475 return;
476 }
477
478 /* Build Unibus tables */
479
480 t_stat build_ubus_tab (DEVICE *dptr, DIB *dibp)
481 {
482 int32 i, idx, vec, ilvl, ibit;
483
484 if ((dptr == NULL) || (dibp == NULL)) return SCPE_IERR; /* validate args */
485 if (dibp->vnum > VEC_DEVMAX) return SCPE_IERR;
486 for (i = 0; i < dibp->vnum; i++) { /* loop thru vec */
487 idx = dibp->vloc + i; /* vector index */
488 vec = dibp->vec? (dibp->vec + (i * 4)): 0; /* vector addr */
489 ilvl = idx / 32;
490 ibit = idx % 32;
491 if ((int_ack[ilvl][ibit] && dibp->ack[i] && /* conflict? */
492 (int_ack[ilvl][ibit] != dibp->ack[i])) ||
493 (int_vec[ilvl][ibit] && vec &&
494 (int_vec[ilvl][ibit] != vec))) {
495 printf ("Device %s interrupt slot conflict at %d\n",
496 sim_dname (dptr), idx);
497 if (sim_log) fprintf (sim_log,
498 "Device %s interrupt slot conflict at %d\n",
499 sim_dname (dptr), idx);
500 return SCPE_STOP;
501 }
502 if (dibp->ack[i]) int_ack[ilvl][ibit] = dibp->ack[i];
503 else if (vec) int_vec[ilvl][ibit] = vec;
504 }
505 for (i = 0; i < (int32) dibp->lnt; i = i + 2) { /* create entries */
506 idx = ((dibp->ba + i) & IOPAGEMASK) >> 1; /* index into disp */
507 if ((iodispR[idx] && dibp->rd && /* conflict? */
508 (iodispR[idx] != dibp->rd)) ||
509 (iodispW[idx] && dibp->wr &&
510 (iodispW[idx] != dibp->wr))) {
511 printf ("Device %s address conflict at %08o\n",
512 sim_dname (dptr), dibp->ba);
513 if (sim_log) fprintf (sim_log,
514 "Device %s address conflict at %08o\n",
515 sim_dname (dptr), dibp->ba);
516 return SCPE_STOP;
517 }
518 if (dibp->rd) iodispR[idx] = dibp->rd; /* set rd dispatch */
519 if (dibp->wr) iodispW[idx] = dibp->wr; /* set wr dispatch */
520 iodibp[idx] = dibp; /* remember DIB */
521 }
522 return SCPE_OK;
523 }
524
525 /* Build tables from device list */
526
527 t_stat build_dib_tab (void)
528 {
529 int32 i;
530 DEVICE *dptr;
531 DIB *dibp;
532 t_stat r;
533
534 init_ubus_tab (); /* init Unibus tables */
535 init_mbus_tab (); /* init Massbus tables */
536 for (i = 0; i < 7; i++) /* seed PIRQ intr */
537 int_vec[i + 1][pirq_bit[i]] = VEC_PIRQ;
538 if (r = cpu_build_dib ()) return r; /* build CPU entries */
539 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */
540 dibp = (DIB *) dptr->ctxt; /* get DIB */
541 if (dibp && !(dptr->flags & DEV_DIS)) { /* defined, enabled? */
542 if (dptr->flags & DEV_MBUS) { /* Massbus? */
543 if (r = build_mbus_tab (dptr, dibp)) /* add to Mbus tab */
544 return r;
545 }
546 else { /* no, Unibus */
547 if (r = build_ubus_tab (dptr, dibp)) /* add to Unibus tab */
548 return r;
549 }
550 } /* end if enabled */
551 } /* end for */
552 return SCPE_OK;
553 }
554
555 /* Show IO space */
556
557 t_stat show_iospace (FILE *st, UNIT *uptr, int32 val, void *desc)
558 {
559 uint32 i, j;
560 DEVICE *dptr;
561 DIB *dibp;
562
563 if (build_dib_tab ()) return SCPE_OK; /* build IO page */
564 for (i = 0, dibp = NULL; i < (IOPAGESIZE >> 1); i++) { /* loop thru entries */
565 if (iodibp[i] && (iodibp[i] != dibp)) { /* new block? */
566 dibp = iodibp[i]; /* DIB for block */
567 for (j = 0, dptr = NULL; sim_devices[j] != NULL; j++) {
568 if (((DIB*) sim_devices[j]->ctxt) == dibp) {
569 dptr = sim_devices[j]; /* locate device */
570 break;
571 } /* end if */
572 } /* end for j */
573 fprintf (st, "%08o - %08o%c\t%s\n", /* print block entry */
574 dibp->ba, dibp->ba + dibp->lnt - 1,
575 (dptr && (dptr->flags & DEV_FLTA))? '*': ' ',
576 dptr? sim_dname (dptr): "CPU");
577 } /* end if */
578 } /* end for i */
579 return SCPE_OK;
580 }
581
582 /* Autoconfiguration
583
584 The table reflects the MicroVAX 3900 microcode, with one addition - the
585 number of controllers field handles devices where multiple instances
586 are simulated through a single DEVICE structure (e.g., DZ, VH).
587
588 A minus number of vectors indicates a field that should be calculated
589 but not placed in the DIB (RQ, TQ dynamic vectors) */
590
591 #define AUTO_MAXC 4
592 #define AUTO_CSRBASE 0010
593 #define AUTO_VECBASE 0300
594
595 typedef struct {
596 char *dnam[AUTO_MAXC];
597 int32 numc;
598 int32 numv;
599 uint32 amod;
600 uint32 vmod;
601 uint32 fixa[AUTO_MAXC];
602 uint32 fixv[AUTO_MAXC];
603 } AUTO_CON;
604
605 AUTO_CON auto_tab[] = {
606 { { "DCI" }, DCX_LINES, 2, 0, 8, { 0 } }, /* DC11 - fx CSRs */
607 { { "DLI" }, DLX_LINES, 2, 0, 8, { 0 } }, /* KL11/DL11/DLV11 - fx CSRs */
608 { { NULL }, 1, 2, 8, 8 }, /* DJ11 */
609 { { NULL }, 1, 2, 16, 8 }, /* DH11 */
610 { { NULL }, 1, 2, 8, 8 }, /* DQ11 */
611 { { NULL }, 1, 2, 8, 8 }, /* DU11 */
612 { { NULL }, 1, 2, 8, 8 }, /* DUP11 */
613 { { NULL }, 10, 2, 8, 8 }, /* LK11A */
614 { { NULL }, 1, 2, 8, 8 }, /* DMC11 */
615 { { "DZ" }, DZ_MUXES, 2, 8, 8 }, /* DZ11 */
616 { { NULL }, 1, 2, 8, 8 }, /* KMC11 */
617 { { NULL }, 1, 2, 8, 8 }, /* LPP11 */
618 { { NULL }, 1, 2, 8, 8 }, /* VMV21 */
619 { { NULL }, 1, 2, 16, 8 }, /* VMV31 */
620 { { NULL }, 1, 2, 8, 8 }, /* DWR70 */
621 { { "RL", "RLB" }, 1, 1, 8, 4, {IOBA_RL}, {VEC_RL} }, /* RL11 */
622 { { "TS", "TSB", "TSC", "TSD" }, 1, 1, 0, 4, /* TS11 */
623 {IOBA_TS, IOBA_TS + 4, IOBA_TS + 8, IOBA_TS + 12},
624 {VEC_TS} },
625 { { NULL }, 1, 2, 16, 8 }, /* LPA11K */
626 { { NULL }, 1, 2, 8, 8 }, /* KW11C */
627 { { NULL }, 1, 1, 8, 8 }, /* reserved */
628 { { "RX", "RY" }, 1, 1, 8, 4, {IOBA_RX} , {VEC_RX} }, /* RX11/RX211 */
629 { { NULL }, 1, 1, 8, 4 }, /* DR11W */
630 { { NULL }, 1, 1, 8, 4, { 0, 0 }, { 0 } }, /* DR11B - fx CSRs,vec */
631 { { NULL }, 1, 2, 8, 8 }, /* DMP11 */
632 { { NULL }, 1, 2, 8, 8 }, /* DPV11 */
633 { { NULL }, 1, 2, 8, 8 }, /* ISB11 */
634 { { NULL }, 1, 2, 16, 8 }, /* DMV11 */
635 { { "XU", "XUB" }, 1, 1, 8, 4, {IOBA_XU}, {VEC_XU} }, /* DEUNA */
636 { { "XQ", "XQB" }, 1, 1, 0, 4, /* DEQNA */
637 {IOBA_XQ,IOBA_XQB}, {VEC_XQ} },
638 { { "RQ", "RQB", "RQC", "RQD" }, 1, -1, 4, 4, /* RQDX3 */
639 {IOBA_RQ}, {VEC_RQ} },
640 { { NULL }, 1, 8, 32, 4 }, /* DMF32 */
641 { { NULL }, 1, 2, 16, 8 }, /* KMS11 */
642 { { NULL }, 1, 1, 16, 4 }, /* VS100 */
643 { { "TQ", "TQB" }, 1, -1, 4, 4, {IOBA_TQ}, {VEC_TQ} }, /* TQK50 */
644 { { NULL }, 1, 2, 16, 8 }, /* KMV11 */
645 { { "VH" }, VH_MUXES, 2, 16, 8 }, /* DHU11/DHQ11 */
646 { { NULL }, 1, 6, 32, 4 }, /* DMZ32 */
647 { { NULL }, 1, 6, 32, 4 }, /* CP132 */
648 { { NULL }, 1, 2, 64, 8, { 0 } }, /* QVSS - fx CSR */
649 { { NULL }, 1, 1, 8, 4 }, /* VS31 */
650 { { NULL }, 1, 1, 0, 4, { 0 } }, /* LNV11 - fx CSR */
651 { { NULL }, 1, 1, 16, 4 }, /* LNV21/QPSS */
652 { { NULL }, 1, 1, 8, 4, { 0 } }, /* QTA - fx CSR */
653 { { NULL }, 1, 1, 8, 4 }, /* DSV11 */
654 { { NULL }, 1, 2, 8, 8 }, /* CSAM */
655 { { NULL }, 1, 2, 8, 8 }, /* ADV11C */
656 { { NULL }, 1, 0, 8, 0 }, /* AAV11C */
657 { { NULL }, 1, 2, 8, 8, { 0 }, { 0 } }, /* AXV11C - fx CSR,vec */
658 { { NULL }, 1, 2, 4, 8, { 0 } }, /* KWV11C - fx CSR */
659 { { NULL }, 1, 2, 8, 8, { 0 } }, /* ADV11D - fx CSR */
660 { { NULL }, 1, 2, 8, 8, { 0 } }, /* AAV11D - fx CSR */
661 /* { { "QDSS" }, 1, 3, 0, 16, {IOBA_QDSS} }, /* QDSS - fx CSR */
662 { { NULL }, -1 } /* end table */
663 };
664
665 t_stat auto_config (char *name, int32 nctrl)
666 {
667 uint32 csr = IOPAGEBASE + AUTO_CSRBASE;
668 uint32 vec = VEC_Q + AUTO_VECBASE;
669 AUTO_CON *autp;
670 DEVICE *dptr;
671 DIB *dibp;
672 uint32 j, k, vmask, amask;
673
674 if (autcon_enb == 0) return SCPE_OK; /* enabled? */
675 if (name) { /* updating? */
676 if (nctrl < 0) return SCPE_ARG;
677 for (autp = auto_tab; autp->numc >= 0; autp++) {
678 for (j = 0; (j < AUTO_MAXC) && autp->dnam[j]; j++) {
679 if (strcmp (name, autp->dnam[j]) == 0)
680 autp->numc = nctrl;
681 }
682 }
683 }
684 for (autp = auto_tab; autp->numc >= 0; autp++) { /* loop thru table */
685 if (autp->amod) { /* floating csr? */
686 amask = autp->amod - 1;
687 csr = (csr + amask) & ~amask; /* align csr */
688 }
689 for (j = k = 0; (j < AUTO_MAXC) && autp->dnam[j]; j++) {
690 dptr = find_dev (autp->dnam[j]); /* find ctrl */
691 if ((dptr == NULL) || (dptr->flags & DEV_DIS) ||
692 !(dptr->flags & DEV_FLTA)) continue; /* enabled, floating? */
693 dibp = (DIB *) dptr->ctxt; /* get DIB */
694 if (dibp == NULL) return SCPE_IERR; /* not there??? */
695 if (autp->amod) { /* dyn csr needed? */
696 if (autp->fixa[k]) /* fixed csr avail? */
697 dibp->ba = autp->fixa[k]; /* use it */
698 else { /* no fixed left */
699 dibp->ba = csr; /* set CSR */
700 csr += (autp->numc * autp->amod); /* next CSR */
701 } /* end else */
702 } /* end if dyn csr */
703 if (autp->numv && autp->vmod) { /* dyn vec needed? */
704 uint32 numv = abs (autp->numv); /* get num vec */
705 if (autp->fixv[k]) { /* fixed vec avail? */
706 if (autp->numv > 0)
707 dibp->vec = autp->fixv[k]; /* use it */
708 }
709 else { /* no fixed left */
710 vmask = autp->vmod - 1;
711 vec = (vec + vmask) & ~vmask; /* align vector */
712 if (autp->numv > 0)
713 dibp->vec = vec; /* set vector */
714 vec += (autp->numc * numv * 4);
715 } /* end else */
716 } /* end if dyn vec */
717 k++; /* next instance */
718 } /* end for j */
719 if (autp->amod) csr = csr + 2; /* flt CSR? gap */
720 } /* end for i */
721 return SCPE_OK;
722 }