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