Commit | Line | Data |
---|---|---|
196ba1fc PH |
1 | /* vax_io.c: VAX 3900 Qbus IO simulator\r |
2 | \r | |
3 | Copyright (c) 1998-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 | qba Qbus adapter\r | |
27 | \r | |
28 | 28-May-08 RMS Inlined physical memory routines\r | |
29 | 25-Jan-08 RMS Fixed declarations (from Mark Pizzolato)\r | |
30 | 03-Dec-05 RMS Added SHOW QBA VIRT and ex/dep via map\r | |
31 | 05-Oct-05 RMS Fixed bug in autoconfiguration (missing XU)\r | |
32 | 25-Jul-05 RMS Revised autoconfiguration algorithm and interface\r | |
33 | 30-Sep-04 RMS Revised Qbus interface\r | |
34 | Moved mem_err, crd_err interrupts here from vax_cpu.c\r | |
35 | 09-Sep-04 RMS Integrated powerup into RESET (with -p)\r | |
36 | 05-Sep-04 RMS Added CRD interrupt handling\r | |
37 | 28-May-04 RMS Revised I/O dispatching (from John Dundas)\r | |
38 | 21-Mar-04 RMS Added RXV21 support\r | |
39 | 21-Dec-03 RMS Fixed bug in autoconfigure vector assignment; added controls\r | |
40 | 21-Nov-03 RMS Added check for interrupt slot conflict (found by Dave Hittner)\r | |
41 | 29-Oct-03 RMS Fixed WriteX declaration (found by Mark Pizzolato)\r | |
42 | 19-Apr-03 RMS Added optimized byte and word DMA routines\r | |
43 | 12-Mar-03 RMS Added logical name support\r | |
44 | 22-Dec-02 RMS Added console halt support\r | |
45 | 12-Oct-02 RMS Added autoconfigure support\r | |
46 | Added SHOW IO space routine\r | |
47 | 29-Sep-02 RMS Added dynamic table support\r | |
48 | 07-Sep-02 RMS Added TMSCP and variable vector support\r | |
49 | */\r | |
50 | \r | |
51 | #include "vax_defs.h"\r | |
52 | \r | |
53 | /* CQBIC system configuration register */\r | |
54 | \r | |
55 | #define CQSCR_POK 0x00008000 /* power ok RO1 */\r | |
56 | #define CQSCR_BHL 0x00004000 /* BHALT enb */\r | |
57 | #define CQSCR_AUX 0x00000400 /* aux mode RONI */\r | |
58 | #define CQSCR_DBO 0x0000000C /* offset NI */\r | |
59 | #define CQSCR_RW (CQSCR_BHL | CQSCR_DBO)\r | |
60 | #define CQSCR_MASK (CQSCR_RW | CQSCR_POK | CQSCR_AUX)\r | |
61 | \r | |
62 | /* CQBIC DMA system error register - W1C */\r | |
63 | \r | |
64 | #define CQDSER_BHL 0x00008000 /* BHALT NI */\r | |
65 | #define CQDSER_DCN 0x00004000 /* DC ~OK NI */\r | |
66 | #define CQDSER_MNX 0x00000080 /* master NXM */\r | |
67 | #define CQDSER_MPE 0x00000020 /* master par NI */\r | |
68 | #define CQDSER_SME 0x00000010 /* slv mem err NI */\r | |
69 | #define CQDSER_LST 0x00000008 /* lost err */\r | |
70 | #define CQDSER_TMO 0x00000004 /* no grant NI */\r | |
71 | #define CQDSER_SNX 0x00000001 /* slave NXM */\r | |
72 | #define CQDSER_ERR (CQDSER_MNX | CQDSER_MPE | CQDSER_TMO | CQDSER_SNX)\r | |
73 | #define CQDSER_MASK 0x0000C0BD\r | |
74 | \r | |
75 | /* CQBIC master error address register */\r | |
76 | \r | |
77 | #define CQMEAR_MASK 0x00001FFF /* Qbus page */\r | |
78 | \r | |
79 | /* CQBIC slave error address register */\r | |
80 | \r | |
81 | #define CQSEAR_MASK 0x000FFFFF /* mem page */\r | |
82 | \r | |
83 | /* CQBIC map base register */\r | |
84 | \r | |
85 | #define CQMBR_MASK 0x1FFF8000 /* 32KB aligned */\r | |
86 | \r | |
87 | /* CQBIC IPC register */\r | |
88 | \r | |
89 | #define CQIPC_QME 0x00008000 /* Qbus read NXM W1C */\r | |
90 | #define CQIPC_INV 0x00004000 /* CAM inval NIWO */\r | |
91 | #define CQIPC_AHLT 0x00000100 /* aux halt NI */\r | |
92 | #define CQIPC_DBIE 0x00000040 /* dbell int enb NI */\r | |
93 | #define CQIPC_LME 0x00000020 /* local mem enb */\r | |
94 | #define CQIPC_DB 0x00000001 /* doorbell req NI */\r | |
95 | #define CQIPC_W1C CQIPC_QME\r | |
96 | #define CQIPC_RW (CQIPC_AHLT | CQIPC_DBIE | CQIPC_LME | CQIPC_DB)\r | |
97 | #define CQIPC_MASK (CQIPC_RW | CQIPC_QME )\r | |
98 | \r | |
99 | /* CQBIC map entry */\r | |
100 | \r | |
101 | #define CQMAP_VLD 0x80000000 /* valid */\r | |
102 | #define CQMAP_PAG 0x000FFFFF /* mem page */\r | |
103 | \r | |
104 | int32 int_req[IPL_HLVL] = { 0 }; /* intr, IPL 14-17 */\r | |
105 | int32 cq_scr = 0; /* SCR */\r | |
106 | int32 cq_dser = 0; /* DSER */\r | |
107 | int32 cq_mear = 0; /* MEAR */\r | |
108 | int32 cq_sear = 0; /* SEAR */\r | |
109 | int32 cq_mbr = 0; /* MBR */\r | |
110 | int32 cq_ipc = 0; /* IPC */\r | |
111 | int32 autcon_enb = 1; /* autoconfig enable */\r | |
112 | \r | |
113 | extern uint32 *M;\r | |
114 | extern UNIT cpu_unit;\r | |
115 | extern int32 PSL, SISR, trpirq, mem_err, crd_err, hlt_pin;\r | |
116 | extern int32 p1;\r | |
117 | extern int32 ssc_bto;\r | |
118 | extern jmp_buf save_env;\r | |
119 | extern int32 sim_switches;\r | |
120 | extern DEVICE *sim_devices[];\r | |
121 | extern FILE *sim_log;\r | |
122 | \r | |
123 | t_stat dbl_rd (int32 *data, int32 addr, int32 access);\r | |
124 | t_stat dbl_wr (int32 data, int32 addr, int32 access);\r | |
125 | int32 eval_int (void);\r | |
126 | void cq_merr (int32 pa);\r | |
127 | void cq_serr (int32 pa);\r | |
128 | t_stat qba_reset (DEVICE *dptr);\r | |
129 | t_stat qba_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw);\r | |
130 | t_stat qba_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw);\r | |
131 | t_bool qba_map_addr (uint32 qa, uint32 *ma);\r | |
132 | t_bool qba_map_addr_c (uint32 qa, uint32 *ma);\r | |
133 | t_stat set_autocon (UNIT *uptr, int32 val, char *cptr, void *desc);\r | |
134 | t_stat show_autocon (FILE *st, UNIT *uptr, int32 val, void *desc);\r | |
135 | t_stat show_iospace (FILE *st, UNIT *uptr, int32 val, void *desc);\r | |
136 | t_stat qba_show_virt (FILE *of, UNIT *uptr, int32 val, void *desc);\r | |
137 | \r | |
138 | /* Qbus adapter data structures\r | |
139 | \r | |
140 | qba_dev QBA device descriptor\r | |
141 | qba_unit QBA units\r | |
142 | qba_reg QBA register list\r | |
143 | */\r | |
144 | \r | |
145 | DIB qba_dib = { IOBA_DBL, IOLN_DBL, &dbl_rd, &dbl_wr, 0 };\r | |
146 | \r | |
147 | UNIT qba_unit = { UDATA (NULL, 0, 0) };\r | |
148 | \r | |
149 | REG qba_reg[] = {\r | |
150 | { HRDATA (SCR, cq_scr, 16) },\r | |
151 | { HRDATA (DSER, cq_dser, 8) },\r | |
152 | { HRDATA (MEAR, cq_mear, 13) },\r | |
153 | { HRDATA (SEAR, cq_sear, 20) },\r | |
154 | { HRDATA (MBR, cq_mbr, 29) },\r | |
155 | { HRDATA (IPC, cq_ipc, 16) },\r | |
156 | { HRDATA (IPL17, int_req[3], 32), REG_RO },\r | |
157 | { HRDATA (IPL16, int_req[2], 32), REG_RO },\r | |
158 | { HRDATA (IPL15, int_req[1], 32), REG_RO },\r | |
159 | { HRDATA (IPL14, int_req[0], 32), REG_RO },\r | |
160 | { FLDATA (AUTOCON, autcon_enb, 0), REG_HRO },\r | |
161 | { NULL }\r | |
162 | };\r | |
163 | \r | |
164 | MTAB qba_mod[] = {\r | |
165 | { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "IOSPACE", NULL,\r | |
166 | NULL, &show_iospace },\r | |
167 | { MTAB_XTD|MTAB_VDV, 1, "AUTOCONFIG", "AUTOCONFIG",\r | |
168 | &set_autocon, &show_autocon },\r | |
169 | { MTAB_XTD|MTAB_VDV, 0, NULL, "NOAUTOCONFIG",\r | |
170 | &set_autocon, NULL },\r | |
171 | { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "VIRTUAL", NULL,\r | |
172 | NULL, &qba_show_virt },\r | |
173 | { 0 }\r | |
174 | };\r | |
175 | \r | |
176 | DEVICE qba_dev = {\r | |
177 | "QBA", &qba_unit, qba_reg, qba_mod,\r | |
178 | 1, 16, CQMAWIDTH, 2, 16, 16,\r | |
179 | &qba_ex, &qba_dep, &qba_reset,\r | |
180 | NULL, NULL, NULL,\r | |
181 | &qba_dib, DEV_QBUS\r | |
182 | };\r | |
183 | \r | |
184 | /* IO page dispatches */\r | |
185 | \r | |
186 | static t_stat (*iodispR[IOPAGESIZE >> 1])(int32 *dat, int32 ad, int32 md);\r | |
187 | static t_stat (*iodispW[IOPAGESIZE >> 1])(int32 dat, int32 ad, int32 md);\r | |
188 | static DIB *iodibp[IOPAGESIZE >> 1];\r | |
189 | \r | |
190 | /* Interrupt request to interrupt action map */\r | |
191 | \r | |
192 | int32 (*int_ack[IPL_HLVL][32])(); /* int ack routines */\r | |
193 | \r | |
194 | /* Interrupt request to vector map */\r | |
195 | \r | |
196 | int32 int_vec[IPL_HLVL][32]; /* int req to vector */\r | |
197 | \r | |
198 | /* The KA65x handles errors in I/O space as follows\r | |
199 | \r | |
200 | - read: set DSER<7>, latch addr in MEAR, machine check\r | |
201 | - write: set DSER<7>, latch addr in MEAR, MEMERR interrupt\r | |
202 | */\r | |
203 | \r | |
204 | int32 ReadQb (uint32 pa)\r | |
205 | {\r | |
206 | int32 idx, val;\r | |
207 | \r | |
208 | idx = (pa & IOPAGEMASK) >> 1;\r | |
209 | if (iodispR[idx]) {\r | |
210 | iodispR[idx] (&val, pa, READ);\r | |
211 | return val;\r | |
212 | }\r | |
213 | cq_merr (pa);\r | |
214 | MACH_CHECK (MCHK_READ);\r | |
215 | return 0;\r | |
216 | }\r | |
217 | \r | |
218 | void WriteQb (uint32 pa, int32 val, int32 mode)\r | |
219 | {\r | |
220 | int32 idx;\r | |
221 | \r | |
222 | idx = (pa & IOPAGEMASK) >> 1;\r | |
223 | if (iodispW[idx]) {\r | |
224 | iodispW[idx] (val, pa, mode);\r | |
225 | return;\r | |
226 | }\r | |
227 | cq_merr (pa);\r | |
228 | mem_err = 1;\r | |
229 | return;\r | |
230 | }\r | |
231 | \r | |
232 | /* ReadIO - read I/O space\r | |
233 | \r | |
234 | Inputs:\r | |
235 | pa = physical address\r | |
236 | lnt = length (BWLQ)\r | |
237 | Output:\r | |
238 | longword of data\r | |
239 | */\r | |
240 | \r | |
241 | int32 ReadIO (uint32 pa, int32 lnt)\r | |
242 | {\r | |
243 | int32 iod;\r | |
244 | \r | |
245 | iod = ReadQb (pa); /* wd from Qbus */\r | |
246 | if (lnt < L_LONG) iod = iod << ((pa & 2)? 16: 0); /* bw? position */\r | |
247 | else iod = (ReadQb (pa + 2) << 16) | iod; /* lw, get 2nd wd */\r | |
248 | SET_IRQL;\r | |
249 | return iod;\r | |
250 | }\r | |
251 | \r | |
252 | /* WriteIO - write I/O space\r | |
253 | \r | |
254 | Inputs:\r | |
255 | pa = physical address\r | |
256 | val = data to write, right justified in 32b longword\r | |
257 | lnt = length (BWLQ)\r | |
258 | Outputs:\r | |
259 | none\r | |
260 | */\r | |
261 | \r | |
262 | void WriteIO (uint32 pa, int32 val, int32 lnt)\r | |
263 | {\r | |
264 | if (lnt == L_BYTE) WriteQb (pa, val, WRITEB);\r | |
265 | else if (lnt == L_WORD) WriteQb (pa, val, WRITE);\r | |
266 | else {\r | |
267 | WriteQb (pa, val & 0xFFFF, WRITE);\r | |
268 | WriteQb (pa + 2, (val >> 16) & 0xFFFF, WRITE);\r | |
269 | }\r | |
270 | SET_IRQL;\r | |
271 | return;\r | |
272 | }\r | |
273 | \r | |
274 | /* Find highest priority outstanding interrupt */\r | |
275 | \r | |
276 | int32 eval_int (void)\r | |
277 | {\r | |
278 | int32 ipl = PSL_GETIPL (PSL);\r | |
279 | int32 i, t;\r | |
280 | \r | |
281 | static const int32 sw_int_mask[IPL_SMAX] = {\r | |
282 | 0xFFFE, 0xFFFC, 0xFFF8, 0xFFF0, /* 0 - 3 */\r | |
283 | 0xFFE0, 0xFFC0, 0xFF80, 0xFF00, /* 4 - 7 */\r | |
284 | 0xFE00, 0xFC00, 0xF800, 0xF000, /* 8 - B */\r | |
285 | 0xE000, 0xC000, 0x8000 /* C - E */\r | |
286 | };\r | |
287 | \r | |
288 | if (hlt_pin) return IPL_HLTPIN; /* hlt pin int */\r | |
289 | if ((ipl < IPL_MEMERR) && mem_err) return IPL_MEMERR; /* mem err int */\r | |
290 | if ((ipl < IPL_CRDERR) && crd_err) return IPL_CRDERR; /* crd err int */\r | |
291 | for (i = IPL_HMAX; i >= IPL_HMIN; i--) { /* chk hwre int */\r | |
292 | if (i <= ipl) return 0; /* at ipl? no int */\r | |
293 | if (int_req[i - IPL_HMIN]) return i; /* req != 0? int */\r | |
294 | }\r | |
295 | if (ipl >= IPL_SMAX) return 0; /* ipl >= sw max? */\r | |
296 | if ((t = SISR & sw_int_mask[ipl]) == 0) return 0; /* eligible req */\r | |
297 | for (i = IPL_SMAX; i > ipl; i--) { /* check swre int */\r | |
298 | if ((t >> i) & 1) return i; /* req != 0? int */\r | |
299 | }\r | |
300 | return 0;\r | |
301 | }\r | |
302 | \r | |
303 | /* Return vector for highest priority hardware interrupt at IPL lvl */\r | |
304 | \r | |
305 | int32 get_vector (int32 lvl)\r | |
306 | {\r | |
307 | int32 i;\r | |
308 | int32 l = lvl - IPL_HMIN;\r | |
309 | \r | |
310 | if (lvl == IPL_MEMERR) { /* mem error? */\r | |
311 | mem_err = 0;\r | |
312 | return SCB_MEMERR;\r | |
313 | }\r | |
314 | if (lvl == IPL_CRDERR) { /* CRD error? */\r | |
315 | crd_err = 0;\r | |
316 | return SCB_CRDERR;\r | |
317 | }\r | |
318 | if (lvl > IPL_HMAX) { /* error req lvl? */\r | |
319 | ABORT (STOP_UIPL); /* unknown intr */\r | |
320 | }\r | |
321 | for (i = 0; int_req[l] && (i < 32); i++) {\r | |
322 | if ((int_req[l] >> i) & 1) {\r | |
323 | int_req[l] = int_req[l] & ~(1u << i);\r | |
324 | if (int_ack[l][i]) return int_ack[l][i]();\r | |
325 | return int_vec[l][i];\r | |
326 | }\r | |
327 | }\r | |
328 | return 0;\r | |
329 | }\r | |
330 | \r | |
331 | /* CQBIC registers\r | |
332 | \r | |
333 | SCR system configuration register\r | |
334 | DSER DMA system error register (W1C)\r | |
335 | MEAR master error address register (RO)\r | |
336 | SEAR slave error address register (RO)\r | |
337 | MBR map base register\r | |
338 | IPC inter-processor communication register\r | |
339 | */\r | |
340 | \r | |
341 | int32 cqbic_rd (int32 pa)\r | |
342 | {\r | |
343 | int32 rg = (pa - CQBICBASE) >> 2;\r | |
344 | \r | |
345 | switch (rg) {\r | |
346 | \r | |
347 | case 0: /* SCR */\r | |
348 | return (cq_scr | CQSCR_POK) & CQSCR_MASK;\r | |
349 | \r | |
350 | case 1: /* DSER */\r | |
351 | return cq_dser & CQDSER_MASK;\r | |
352 | \r | |
353 | case 2: /* MEAR */\r | |
354 | return cq_mear & CQMEAR_MASK;\r | |
355 | \r | |
356 | case 3: /* SEAR */\r | |
357 | return cq_sear & CQSEAR_MASK;\r | |
358 | \r | |
359 | case 4: /* MBR */\r | |
360 | return cq_mbr & CQMBR_MASK;\r | |
361 | }\r | |
362 | \r | |
363 | return 0;\r | |
364 | }\r | |
365 | \r | |
366 | void cqbic_wr (int32 pa, int32 val, int32 lnt)\r | |
367 | {\r | |
368 | int32 nval, rg = (pa - CQBICBASE) >> 2;\r | |
369 | \r | |
370 | if (lnt < L_LONG) {\r | |
371 | int32 sc = (pa & 3) << 3;\r | |
372 | int32 mask = (lnt == L_WORD)? 0xFFFF: 0xFF;\r | |
373 | int32 t = cqbic_rd (pa);\r | |
374 | nval = ((val & mask) << sc) | (t & ~(mask << sc));\r | |
375 | val = val << sc;\r | |
376 | }\r | |
377 | else nval = val;\r | |
378 | switch (rg) {\r | |
379 | \r | |
380 | case 0: /* SCR */\r | |
381 | cq_scr = ((cq_scr & ~CQSCR_RW) | (nval & CQSCR_RW)) & CQSCR_MASK;\r | |
382 | break;\r | |
383 | \r | |
384 | case 1: /* DSER */\r | |
385 | cq_dser = (cq_dser & ~val) & CQDSER_MASK;\r | |
386 | if (val & CQDSER_SME) cq_ipc = cq_ipc & ~CQIPC_QME;\r | |
387 | break;\r | |
388 | \r | |
389 | case 2: case 3:\r | |
390 | cq_merr (pa); /* MEAR, SEAR */\r | |
391 | MACH_CHECK (MCHK_WRITE);\r | |
392 | break;\r | |
393 | \r | |
394 | case 4: /* MBR */\r | |
395 | cq_mbr = nval & CQMBR_MASK;\r | |
396 | break;\r | |
397 | }\r | |
398 | return;\r | |
399 | }\r | |
400 | \r | |
401 | /* IPC can be read as local register or as Qbus I/O\r | |
402 | Because of the W1C */\r | |
403 | \r | |
404 | int32 cqipc_rd (int32 pa)\r | |
405 | {\r | |
406 | return cq_ipc & CQIPC_MASK; /* IPC */\r | |
407 | }\r | |
408 | \r | |
409 | void cqipc_wr (int32 pa, int32 val, int32 lnt)\r | |
410 | {\r | |
411 | int32 nval = val;\r | |
412 | \r | |
413 | if (lnt < L_LONG) {\r | |
414 | int32 sc = (pa & 3) << 3;\r | |
415 | nval = val << sc;\r | |
416 | }\r | |
417 | \r | |
418 | cq_ipc = cq_ipc & ~(nval & CQIPC_W1C); /* W1C */\r | |
419 | if ((pa & 3) == 0) /* low byte only */\r | |
420 | cq_ipc = ((cq_ipc & ~CQIPC_RW) | (val & CQIPC_RW)) & CQIPC_MASK;\r | |
421 | return;\r | |
422 | }\r | |
423 | \r | |
424 | /* I/O page routines */\r | |
425 | \r | |
426 | t_stat dbl_rd (int32 *data, int32 addr, int32 access)\r | |
427 | {\r | |
428 | *data = cq_ipc & CQIPC_MASK;\r | |
429 | return SCPE_OK;\r | |
430 | }\r | |
431 | \r | |
432 | t_stat dbl_wr (int32 data, int32 addr, int32 access)\r | |
433 | {\r | |
434 | cqipc_wr (addr, data, (access == WRITEB)? L_BYTE: L_WORD);\r | |
435 | return SCPE_OK;\r | |
436 | }\r | |
437 | \r | |
438 | /* CQBIC map read and write (reflects to main memory)\r | |
439 | \r | |
440 | Read error: set DSER<0>, latch slave address, machine check\r | |
441 | Write error: set DSER<0>, latch slave address, memory error interrupt\r | |
442 | */\r | |
443 | \r | |
444 | int32 cqmap_rd (int32 pa)\r | |
445 | {\r | |
446 | int32 ma = (pa & CQMAPAMASK) + cq_mbr; /* mem addr */\r | |
447 | \r | |
448 | if (ADDR_IS_MEM (ma)) return M[ma >> 2];\r | |
449 | cq_serr (ma); /* set err */\r | |
450 | MACH_CHECK (MCHK_READ); /* mcheck */\r | |
451 | return 0;\r | |
452 | }\r | |
453 | \r | |
454 | void cqmap_wr (int32 pa, int32 val, int32 lnt)\r | |
455 | {\r | |
456 | int32 ma = (pa & CQMAPAMASK) + cq_mbr; /* mem addr */\r | |
457 | \r | |
458 | if (ADDR_IS_MEM (ma)) {\r | |
459 | if (lnt < L_LONG) {\r | |
460 | int32 sc = (pa & 3) << 3;\r | |
461 | int32 mask = (lnt == L_WORD)? 0xFFFF: 0xFF;\r | |
462 | int32 t = M[ma >> 2];\r | |
463 | val = ((val & mask) << sc) | (t & ~(mask << sc));\r | |
464 | }\r | |
465 | M[ma >> 2] = val;\r | |
466 | }\r | |
467 | else {\r | |
468 | cq_serr (ma); /* error */\r | |
469 | mem_err = 1;\r | |
470 | }\r | |
471 | return;\r | |
472 | }\r | |
473 | \r | |
474 | /* CQBIC Qbus memory read and write (reflects to main memory)\r | |
475 | \r | |
476 | May give master or slave error, depending on where the failure occurs\r | |
477 | */\r | |
478 | \r | |
479 | int32 cqmem_rd (int32 pa)\r | |
480 | {\r | |
481 | int32 qa = pa & CQMAMASK; /* Qbus addr */\r | |
482 | uint32 ma;\r | |
483 | \r | |
484 | if (qba_map_addr (qa, &ma)) return M[ma >> 2]; /* map addr */\r | |
485 | MACH_CHECK (MCHK_READ); /* err? mcheck */\r | |
486 | return 0;\r | |
487 | }\r | |
488 | \r | |
489 | void cqmem_wr (int32 pa, int32 val, int32 lnt)\r | |
490 | {\r | |
491 | int32 qa = pa & CQMAMASK; /* Qbus addr */\r | |
492 | uint32 ma;\r | |
493 | \r | |
494 | if (qba_map_addr (qa, &ma)) { /* map addr */\r | |
495 | if (lnt < L_LONG) {\r | |
496 | int32 sc = (pa & 3) << 3;\r | |
497 | int32 mask = (lnt == L_WORD)? 0xFFFF: 0xFF;\r | |
498 | int32 t = M[ma >> 2];\r | |
499 | val = ((val & mask) << sc) | (t & ~(mask << sc));\r | |
500 | }\r | |
501 | M[ma >> 2] = val;\r | |
502 | }\r | |
503 | else mem_err = 1;\r | |
504 | return;\r | |
505 | }\r | |
506 | \r | |
507 | /* Map an address via the translation map */\r | |
508 | \r | |
509 | t_bool qba_map_addr (uint32 qa, uint32 *ma)\r | |
510 | {\r | |
511 | int32 qblk = (qa >> VA_V_VPN); /* Qbus blk */\r | |
512 | int32 qmma = ((qblk << 2) & CQMAPAMASK) + cq_mbr; /* map entry */\r | |
513 | \r | |
514 | if (ADDR_IS_MEM (qmma)) { /* legit? */\r | |
515 | int32 qmap = M[qmma >> 2]; /* get map */\r | |
516 | if (qmap & CQMAP_VLD) { /* valid? */\r | |
517 | *ma = ((qmap & CQMAP_PAG) << VA_V_VPN) + VA_GETOFF (qa);\r | |
518 | if (ADDR_IS_MEM (*ma)) return TRUE; /* legit addr */\r | |
519 | cq_serr (*ma); /* slave nxm */\r | |
520 | return FALSE;\r | |
521 | }\r | |
522 | cq_merr (qa); /* master nxm */\r | |
523 | return FALSE;\r | |
524 | }\r | |
525 | cq_serr (0); /* inv mem */\r | |
526 | return FALSE;\r | |
527 | }\r | |
528 | \r | |
529 | /* Map an address via the translation map - console version (no status changes) */\r | |
530 | \r | |
531 | t_bool qba_map_addr_c (uint32 qa, uint32 *ma)\r | |
532 | {\r | |
533 | int32 qblk = (qa >> VA_V_VPN); /* Qbus blk */\r | |
534 | int32 qmma = ((qblk << 2) & CQMAPAMASK) + cq_mbr; /* map entry */\r | |
535 | \r | |
536 | if (ADDR_IS_MEM (qmma)) { /* legit? */\r | |
537 | int32 qmap = M[qmma >> 2]; /* get map */\r | |
538 | if (qmap & CQMAP_VLD) { /* valid? */\r | |
539 | *ma = ((qmap & CQMAP_PAG) << VA_V_VPN) + VA_GETOFF (qa);\r | |
540 | return TRUE; /* legit addr */\r | |
541 | }\r | |
542 | }\r | |
543 | return FALSE;\r | |
544 | }\r | |
545 | \r | |
546 | /* Set master error */\r | |
547 | \r | |
548 | void cq_merr (int32 pa)\r | |
549 | {\r | |
550 | if (cq_dser & CQDSER_ERR) cq_dser = cq_dser | CQDSER_LST;\r | |
551 | cq_dser = cq_dser | CQDSER_MNX; /* master nxm */\r | |
552 | cq_mear = (pa >> VA_V_VPN) & CQMEAR_MASK; /* page addr */\r | |
553 | return;\r | |
554 | }\r | |
555 | \r | |
556 | /* Set slave error */\r | |
557 | \r | |
558 | void cq_serr (int32 pa)\r | |
559 | {\r | |
560 | if (cq_dser & CQDSER_ERR) cq_dser = cq_dser | CQDSER_LST;\r | |
561 | cq_dser = cq_dser | CQDSER_SNX; /* slave nxm */\r | |
562 | cq_sear = (pa >> VA_V_VPN) & CQSEAR_MASK;\r | |
563 | return;\r | |
564 | }\r | |
565 | \r | |
566 | /* Reset I/O bus */\r | |
567 | \r | |
568 | void ioreset_wr (int32 data)\r | |
569 | {\r | |
570 | reset_all (5); /* from qba on... */\r | |
571 | return;\r | |
572 | }\r | |
573 | \r | |
574 | /* Powerup CQBIC */\r | |
575 | \r | |
576 | t_stat qba_powerup (void)\r | |
577 | {\r | |
578 | cq_mbr = 0;\r | |
579 | cq_scr = CQSCR_POK;\r | |
580 | return SCPE_OK;\r | |
581 | }\r | |
582 | \r | |
583 | /* Reset CQBIC */\r | |
584 | \r | |
585 | t_stat qba_reset (DEVICE *dptr)\r | |
586 | {\r | |
587 | int32 i;\r | |
588 | \r | |
589 | if (sim_switches & SWMASK ('P')) qba_powerup ();\r | |
590 | cq_scr = (cq_scr & CQSCR_BHL) | CQSCR_POK;\r | |
591 | cq_dser = cq_mear = cq_sear = cq_ipc = 0;\r | |
592 | for (i = 0; i < IPL_HLVL; i++) int_req[i] = 0;\r | |
593 | return SCPE_OK;\r | |
594 | }\r | |
595 | \r | |
596 | /* Qbus I/O buffer routines, aligned access\r | |
597 | \r | |
598 | Map_ReadB - fetch byte buffer from memory\r | |
599 | Map_ReadW - fetch word buffer from memory\r | |
600 | Map_WriteB - store byte buffer into memory\r | |
601 | Map_WriteW - store word buffer into memory\r | |
602 | */\r | |
603 | \r | |
604 | int32 Map_ReadB (uint32 ba, int32 bc, uint8 *buf)\r | |
605 | {\r | |
606 | int32 i;\r | |
607 | uint32 ma, dat;\r | |
608 | \r | |
609 | if ((ba | bc) & 03) { /* check alignment */\r | |
610 | for (i = ma = 0; i < bc; i++, buf++) { /* by bytes */\r | |
611 | if ((ma & VA_M_OFF) == 0) { /* need map? */\r | |
612 | if (!qba_map_addr (ba + i, &ma)) /* inv or NXM? */\r | |
613 | return (bc - i);\r | |
614 | }\r | |
615 | *buf = ReadB (ma);\r | |
616 | ma = ma + 1;\r | |
617 | }\r | |
618 | }\r | |
619 | else {\r | |
620 | for (i = ma = 0; i < bc; i = i + 4, buf++) { /* by longwords */\r | |
621 | if ((ma & VA_M_OFF) == 0) { /* need map? */\r | |
622 | if (!qba_map_addr (ba + i, &ma)) /* inv or NXM? */\r | |
623 | return (bc - i);\r | |
624 | }\r | |
625 | dat = ReadL (ma); /* get lw */\r | |
626 | *buf++ = dat & BMASK; /* low 8b */\r | |
627 | *buf++ = (dat >> 8) & BMASK; /* next 8b */\r | |
628 | *buf++ = (dat >> 16) & BMASK; /* next 8b */\r | |
629 | *buf = (dat >> 24) & BMASK;\r | |
630 | ma = ma + 4;\r | |
631 | }\r | |
632 | }\r | |
633 | return 0;\r | |
634 | }\r | |
635 | \r | |
636 | int32 Map_ReadW (uint32 ba, int32 bc, uint16 *buf)\r | |
637 | {\r | |
638 | int32 i;\r | |
639 | uint32 ma,dat;\r | |
640 | \r | |
641 | ba = ba & ~01;\r | |
642 | bc = bc & ~01;\r | |
643 | if ((ba | bc) & 03) { /* check alignment */\r | |
644 | for (i = ma = 0; i < bc; i = i + 2, buf++) { /* by words */\r | |
645 | if ((ma & VA_M_OFF) == 0) { /* need map? */\r | |
646 | if (!qba_map_addr (ba + i, &ma)) /* inv or NXM? */\r | |
647 | return (bc - i);\r | |
648 | }\r | |
649 | *buf = ReadW (ma);\r | |
650 | ma = ma + 2;\r | |
651 | }\r | |
652 | }\r | |
653 | else {\r | |
654 | for (i = ma = 0; i < bc; i = i + 4, buf++) { /* by longwords */\r | |
655 | if ((ma & VA_M_OFF) == 0) { /* need map? */\r | |
656 | if (!qba_map_addr (ba + i, &ma)) /* inv or NXM? */\r | |
657 | return (bc - i);\r | |
658 | }\r | |
659 | dat = ReadL (ma); /* get lw */\r | |
660 | *buf++ = dat & WMASK; /* low 16b */\r | |
661 | *buf = (dat >> 16) & WMASK; /* high 16b */\r | |
662 | ma = ma + 4;\r | |
663 | }\r | |
664 | }\r | |
665 | return 0;\r | |
666 | }\r | |
667 | \r | |
668 | int32 Map_WriteB (uint32 ba, int32 bc, uint8 *buf)\r | |
669 | {\r | |
670 | int32 i;\r | |
671 | uint32 ma, dat;\r | |
672 | \r | |
673 | if ((ba | bc) & 03) { /* check alignment */\r | |
674 | for (i = ma = 0; i < bc; i++, buf++) { /* by bytes */\r | |
675 | if ((ma & VA_M_OFF) == 0) { /* need map? */\r | |
676 | if (!qba_map_addr (ba + i, &ma)) /* inv or NXM? */\r | |
677 | return (bc - i);\r | |
678 | }\r | |
679 | WriteB (ma, *buf);\r | |
680 | ma = ma + 1;\r | |
681 | }\r | |
682 | }\r | |
683 | else {\r | |
684 | for (i = ma = 0; i < bc; i = i + 4, buf++) { /* by longwords */\r | |
685 | if ((ma & VA_M_OFF) == 0) { /* need map? */\r | |
686 | if (!qba_map_addr (ba + i, &ma)) /* inv or NXM? */\r | |
687 | return (bc - i);\r | |
688 | }\r | |
689 | dat = (uint32) *buf++; /* get low 8b */\r | |
690 | dat = dat | (((uint32) *buf++) << 8); /* merge next 8b */\r | |
691 | dat = dat | (((uint32) *buf++) << 16); /* merge next 8b */\r | |
692 | dat = dat | (((uint32) *buf) << 24); /* merge hi 8b */\r | |
693 | WriteL (ma, dat); /* store lw */\r | |
694 | ma = ma + 4;\r | |
695 | }\r | |
696 | }\r | |
697 | return 0;\r | |
698 | }\r | |
699 | \r | |
700 | int32 Map_WriteW (uint32 ba, int32 bc, uint16 *buf)\r | |
701 | {\r | |
702 | int32 i;\r | |
703 | uint32 ma, dat;\r | |
704 | \r | |
705 | ba = ba & ~01;\r | |
706 | bc = bc & ~01;\r | |
707 | if ((ba | bc) & 03) { /* check alignment */\r | |
708 | for (i = ma = 0; i < bc; i = i + 2, buf++) { /* by words */\r | |
709 | if ((ma & VA_M_OFF) == 0) { /* need map? */\r | |
710 | if (!qba_map_addr (ba + i, &ma)) /* inv or NXM? */\r | |
711 | return (bc - i);\r | |
712 | }\r | |
713 | WriteW (ma, *buf);\r | |
714 | ma = ma + 2;\r | |
715 | }\r | |
716 | }\r | |
717 | else {\r | |
718 | for (i = ma = 0; i < bc; i = i + 4, buf++) { /* by longwords */\r | |
719 | if ((ma & VA_M_OFF) == 0) { /* need map? */\r | |
720 | if (!qba_map_addr (ba + i, &ma)) /* inv or NXM? */\r | |
721 | return (bc - i);\r | |
722 | }\r | |
723 | dat = (uint32) *buf++; /* get low 16b */\r | |
724 | dat = dat | (((uint32) *buf) << 16); /* merge hi 16b */\r | |
725 | WriteL (ma, dat); /* store lw */\r | |
726 | ma = ma + 4;\r | |
727 | }\r | |
728 | }\r | |
729 | return 0;\r | |
730 | }\r | |
731 | \r | |
732 | /* Memory examine via map (word only) */\r | |
733 | \r | |
734 | t_stat qba_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw)\r | |
735 | {\r | |
736 | uint32 qa = (uint32) exta, pa;\r | |
737 | \r | |
738 | if ((vptr == NULL) || (qa >= CQMSIZE)) return SCPE_ARG;\r | |
739 | if (qba_map_addr_c (qa, &pa) && ADDR_IS_MEM (pa)) {\r | |
740 | *vptr = (uint32) ReadW (pa);\r | |
741 | return SCPE_OK;\r | |
742 | }\r | |
743 | return SCPE_NXM;\r | |
744 | }\r | |
745 | \r | |
746 | /* Memory deposit via map (word only) */\r | |
747 | \r | |
748 | t_stat qba_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw)\r | |
749 | {\r | |
750 | uint32 qa = (uint32) exta, pa;\r | |
751 | \r | |
752 | if (qa >= CQMSIZE) return SCPE_ARG;\r | |
753 | if (qba_map_addr_c (qa, &pa) && ADDR_IS_MEM (pa)) {\r | |
754 | WriteW (pa, (int32) val);\r | |
755 | return SCPE_OK;\r | |
756 | }\r | |
757 | return SCPE_NXM;\r | |
758 | }\r | |
759 | \r | |
760 | /* Enable/disable autoconfiguration */\r | |
761 | \r | |
762 | t_stat set_autocon (UNIT *uptr, int32 val, char *cptr, void *desc)\r | |
763 | {\r | |
764 | if (cptr != NULL) return SCPE_ARG;\r | |
765 | autcon_enb = val;\r | |
766 | return auto_config (NULL, 0);\r | |
767 | }\r | |
768 | \r | |
769 | /* Show autoconfiguration status */\r | |
770 | \r | |
771 | t_stat show_autocon (FILE *st, UNIT *uptr, int32 val, void *desc)\r | |
772 | {\r | |
773 | fprintf (st, "autoconfiguration ");\r | |
774 | fprintf (st, autcon_enb? "enabled": "disabled");\r | |
775 | return SCPE_OK;\r | |
776 | }\r | |
777 | \r | |
778 | /* Change device address */\r | |
779 | \r | |
780 | t_stat set_addr (UNIT *uptr, int32 val, char *cptr, void *desc)\r | |
781 | {\r | |
782 | DEVICE *dptr;\r | |
783 | DIB *dibp;\r | |
784 | uint32 newba;\r | |
785 | t_stat r;\r | |
786 | \r | |
787 | if (cptr == NULL) return SCPE_ARG;\r | |
788 | if ((val == 0) || (uptr == NULL)) return SCPE_IERR;\r | |
789 | dptr = find_dev_from_unit (uptr);\r | |
790 | if (dptr == NULL) return SCPE_IERR;\r | |
791 | dibp = (DIB *) dptr->ctxt;\r | |
792 | if (dibp == NULL) return SCPE_IERR;\r | |
793 | newba = (uint32) get_uint (cptr, 16, IOPAGEBASE+IOPAGEMASK, &r); /* get new */\r | |
794 | if (r != SCPE_OK) return r;\r | |
795 | if ((newba <= IOPAGEBASE) || /* must be > 0 */\r | |
796 | (newba % ((uint32) val))) return SCPE_ARG; /* check modulus */\r | |
797 | dibp->ba = newba; /* store */\r | |
798 | dptr->flags = dptr->flags & ~DEV_FLTA; /* not floating */\r | |
799 | autcon_enb = 0; /* autoconfig off */\r | |
800 | return SCPE_OK;\r | |
801 | }\r | |
802 | \r | |
803 | /* Show device address */\r | |
804 | \r | |
805 | t_stat show_addr (FILE *st, UNIT *uptr, int32 val, void *desc)\r | |
806 | {\r | |
807 | DEVICE *dptr;\r | |
808 | DIB *dibp;\r | |
809 | \r | |
810 | if (uptr == NULL) return SCPE_IERR;\r | |
811 | dptr = find_dev_from_unit (uptr);\r | |
812 | if (dptr == NULL) return SCPE_IERR;\r | |
813 | dibp = (DIB *) dptr->ctxt;\r | |
814 | if ((dibp == NULL) || (dibp->ba <= IOPAGEBASE)) return SCPE_IERR;\r | |
815 | fprintf (st, "address=%08X", dibp->ba);\r | |
816 | if (dibp->lnt > 1)\r | |
817 | fprintf (st, "-%08X", dibp->ba + dibp->lnt - 1);\r | |
818 | if (dptr->flags & DEV_FLTA) fprintf (st, "*");\r | |
819 | return SCPE_OK;\r | |
820 | }\r | |
821 | \r | |
822 | /* Set address floating */\r | |
823 | \r | |
824 | t_stat set_addr_flt (UNIT *uptr, int32 val, char *cptr, void *desc)\r | |
825 | {\r | |
826 | DEVICE *dptr;\r | |
827 | \r | |
828 | if (cptr == NULL) return SCPE_ARG;\r | |
829 | if ((val == 0) || (uptr == NULL)) return SCPE_IERR;\r | |
830 | dptr = find_dev_from_unit (uptr);\r | |
831 | if (dptr == NULL) return SCPE_IERR;\r | |
832 | dptr->flags = dptr->flags | DEV_FLTA; /* floating */\r | |
833 | return auto_config (NULL, 0); /* autoconfigure */\r | |
834 | }\r | |
835 | \r | |
836 | /* Change device vector */\r | |
837 | \r | |
838 | t_stat set_vec (UNIT *uptr, int32 arg, char *cptr, void *desc)\r | |
839 | {\r | |
840 | DEVICE *dptr;\r | |
841 | DIB *dibp;\r | |
842 | uint32 newvec;\r | |
843 | t_stat r;\r | |
844 | \r | |
845 | if (cptr == NULL) return SCPE_ARG;\r | |
846 | if (uptr == NULL) return SCPE_IERR;\r | |
847 | dptr = find_dev_from_unit (uptr);\r | |
848 | if (dptr == NULL) return SCPE_IERR;\r | |
849 | dibp = (DIB *) dptr->ctxt;\r | |
850 | if (dibp == NULL) return SCPE_IERR;\r | |
851 | newvec = (uint32) get_uint (cptr, 16, VEC_Q + 01000, &r);\r | |
852 | if ((r != SCPE_OK) || (newvec <= VEC_Q) ||\r | |
853 | ((newvec + (dibp->vnum * 4)) >= (VEC_Q + 01000)) ||\r | |
854 | (newvec & ((dibp->vnum > 1)? 07: 03))) return SCPE_ARG;\r | |
855 | dibp->vec = newvec;\r | |
856 | dptr->flags = dptr->flags & ~DEV_FLTA; /* not floating */\r | |
857 | autcon_enb = 0; /* autoconfig off */\r | |
858 | return SCPE_OK;\r | |
859 | }\r | |
860 | \r | |
861 | /* Show device vector */\r | |
862 | \r | |
863 | t_stat show_vec (FILE *st, UNIT *uptr, int32 arg, void *desc)\r | |
864 | {\r | |
865 | DEVICE *dptr;\r | |
866 | DIB *dibp;\r | |
867 | uint32 vec, numvec;\r | |
868 | \r | |
869 | if (uptr == NULL) return SCPE_IERR;\r | |
870 | dptr = find_dev_from_unit (uptr);\r | |
871 | if (dptr == NULL) return SCPE_IERR;\r | |
872 | dibp = (DIB *) dptr->ctxt;\r | |
873 | if (dibp == NULL) return SCPE_IERR;\r | |
874 | vec = dibp->vec;\r | |
875 | if (arg) numvec = arg;\r | |
876 | else numvec = dibp->vnum;\r | |
877 | if (vec == 0) fprintf (st, "no vector");\r | |
878 | else {\r | |
879 | fprintf (st, "vector=%X", vec);\r | |
880 | if (numvec > 1) fprintf (st, "-%X", vec + (4 * (numvec - 1)));\r | |
881 | }\r | |
882 | return SCPE_OK;\r | |
883 | }\r | |
884 | \r | |
885 | /* Build dispatch tables */\r | |
886 | \r | |
887 | t_stat build_dsp_tab (DEVICE *dptr, DIB *dibp)\r | |
888 | {\r | |
889 | uint32 i, idx;\r | |
890 | \r | |
891 | if ((dptr == NULL) || (dibp == NULL)) return SCPE_IERR; /* validate args */\r | |
892 | for (i = 0; i < dibp->lnt; i = i + 2) { /* create entries */\r | |
893 | idx = ((dibp->ba + i) & IOPAGEMASK) >> 1; /* index into disp */\r | |
894 | if ((iodispR[idx] && dibp->rd && /* conflict? */\r | |
895 | (iodispR[idx] != dibp->rd)) ||\r | |
896 | (iodispW[idx] && dibp->wr &&\r | |
897 | (iodispW[idx] != dibp->wr))) {\r | |
898 | printf ("Device %s address conflict at %08o\n",\r | |
899 | sim_dname (dptr), dibp->ba);\r | |
900 | if (sim_log) fprintf (sim_log,\r | |
901 | "Device %s address conflict at %08o\n",\r | |
902 | sim_dname (dptr), dibp->ba);\r | |
903 | return SCPE_STOP;\r | |
904 | }\r | |
905 | if (dibp->rd) iodispR[idx] = dibp->rd; /* set rd dispatch */\r | |
906 | if (dibp->wr) iodispW[idx] = dibp->wr; /* set wr dispatch */\r | |
907 | iodibp[idx] = dibp; /* remember DIB */\r | |
908 | }\r | |
909 | return SCPE_OK;\r | |
910 | }\r | |
911 | \r | |
912 | \r | |
913 | /* Build interrupt tables */\r | |
914 | \r | |
915 | t_stat build_int_vec (DEVICE *dptr, DIB *dibp)\r | |
916 | {\r | |
917 | int32 i, idx, vec, ilvl, ibit;\r | |
918 | \r | |
919 | if ((dptr == NULL) || (dibp == NULL)) return SCPE_IERR; /* validate args */\r | |
920 | if (dibp->vnum > VEC_DEVMAX) return SCPE_IERR;\r | |
921 | for (i = 0; i < dibp->vnum; i++) { /* loop thru vec */\r | |
922 | idx = dibp->vloc + i; /* vector index */\r | |
923 | vec = dibp->vec? (dibp->vec + (i * 4)): 0; /* vector addr */\r | |
924 | ilvl = idx / 32;\r | |
925 | ibit = idx % 32;\r | |
926 | if ((int_ack[ilvl][ibit] && dibp->ack[i] && /* conflict? */\r | |
927 | (int_ack[ilvl][ibit] != dibp->ack[i])) ||\r | |
928 | (int_vec[ilvl][ibit] && vec &&\r | |
929 | (int_vec[ilvl][ibit] != vec))) {\r | |
930 | printf ("Device %s interrupt slot conflict at %d\n",\r | |
931 | sim_dname (dptr), idx);\r | |
932 | if (sim_log) fprintf (sim_log,\r | |
933 | "Device %s interrupt slot conflict at %d\n",\r | |
934 | sim_dname (dptr), idx);\r | |
935 | return SCPE_STOP;\r | |
936 | }\r | |
937 | if (dibp->ack[i]) int_ack[ilvl][ibit] = dibp->ack[i];\r | |
938 | else if (vec) int_vec[ilvl][ibit] = vec;\r | |
939 | }\r | |
940 | return SCPE_OK;\r | |
941 | }\r | |
942 | \r | |
943 | /* Build dib_tab from device list */\r | |
944 | \r | |
945 | t_stat build_dib_tab (void)\r | |
946 | {\r | |
947 | int32 i, j;\r | |
948 | DEVICE *dptr;\r | |
949 | DIB *dibp;\r | |
950 | t_stat r;\r | |
951 | \r | |
952 | for (i = 0; i < IPL_HLVL; i++) { /* clear int tables */\r | |
953 | for (j = 0; j < 32; j++) {\r | |
954 | int_vec[i][j] = 0;\r | |
955 | int_ack[i][j] = NULL;\r | |
956 | }\r | |
957 | }\r | |
958 | for (i = 0; i < (IOPAGESIZE >> 1); i++) { /* clear dispatch tab */\r | |
959 | iodispR[i] = NULL;\r | |
960 | iodispW[i] = NULL;\r | |
961 | iodibp[i] = NULL;\r | |
962 | }\r | |
963 | for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */\r | |
964 | dibp = (DIB *) dptr->ctxt; /* get DIB */\r | |
965 | if (dibp && !(dptr->flags & DEV_DIS)) { /* defined, enabled? */\r | |
966 | if (r = build_int_vec (dptr, dibp)) /* add to intr tab */\r | |
967 | return r;\r | |
968 | if (r = build_dsp_tab (dptr, dibp)) /* add to dispatch tab */\r | |
969 | return r;\r | |
970 | } /* end if enabled */\r | |
971 | } /* end for */\r | |
972 | return SCPE_OK;\r | |
973 | }\r | |
974 | \r | |
975 | /* Show IO space */\r | |
976 | \r | |
977 | t_stat show_iospace (FILE *st, UNIT *uptr, int32 val, void *desc)\r | |
978 | {\r | |
979 | uint32 i, j;\r | |
980 | DEVICE *dptr;\r | |
981 | DIB *dibp;\r | |
982 | \r | |
983 | if (build_dib_tab ()) return SCPE_OK; /* build IO page */\r | |
984 | for (i = 0, dibp = NULL; i < (IOPAGESIZE >> 1); i++) { /* loop thru entries */\r | |
985 | if (iodibp[i] && (iodibp[i] != dibp)) { /* new block? */\r | |
986 | dibp = iodibp[i]; /* DIB for block */\r | |
987 | for (j = 0, dptr = NULL; sim_devices[j] != NULL; j++) {\r | |
988 | if (((DIB*) sim_devices[j]->ctxt) == dibp) {\r | |
989 | dptr = sim_devices[j]; /* locate device */\r | |
990 | break;\r | |
991 | } /* end if */\r | |
992 | } /* end for j */\r | |
993 | fprintf (st, "%08X - %08X%c\t%s\n", dibp->ba,\r | |
994 | dibp->ba + dibp->lnt - 1,\r | |
995 | (dptr && (dptr->flags & DEV_FLTA))? '*': ' ',\r | |
996 | dptr? sim_dname (dptr): "CPU");\r | |
997 | } /* end if */\r | |
998 | } /* end for i */\r | |
999 | return SCPE_OK;\r | |
1000 | }\r | |
1001 | \r | |
1002 | /* Show QBA virtual address */\r | |
1003 | \r | |
1004 | t_stat qba_show_virt (FILE *of, UNIT *uptr, int32 val, void *desc)\r | |
1005 | {\r | |
1006 | t_stat r;\r | |
1007 | char *cptr = (char *) desc;\r | |
1008 | uint32 qa, pa;\r | |
1009 | \r | |
1010 | if (cptr) {\r | |
1011 | qa = (uint32) get_uint (cptr, 16, CQMSIZE - 1, &r);\r | |
1012 | if (r == SCPE_OK) {\r | |
1013 | if (qba_map_addr_c (qa, &pa))\r | |
1014 | fprintf (of, "Qbus %-X = physical %-X\n", qa, pa);\r | |
1015 | else fprintf (of, "Qbus %-X: invalid mapping\n", qa);\r | |
1016 | return SCPE_OK;\r | |
1017 | }\r | |
1018 | }\r | |
1019 | fprintf (of, "Invalid argument\n");\r | |
1020 | return SCPE_OK;\r | |
1021 | }\r | |
1022 | \r | |
1023 | /* Autoconfiguration\r | |
1024 | \r | |
1025 | The table reflects the MicroVAX 3900 microcode, with one addition - the\r | |
1026 | number of controllers field handles devices where multiple instances\r | |
1027 | are simulated through a single DEVICE structure (e.g., DZ, VH).\r | |
1028 | \r | |
1029 | A minus number of vectors indicates a field that should be calculated\r | |
1030 | but not placed in the DIB (RQ, TQ dynamic vectors) */\r | |
1031 | \r | |
1032 | #define AUTO_MAXC 4\r | |
1033 | #define AUTO_CSRBASE 0010\r | |
1034 | #define AUTO_VECBASE 0300\r | |
1035 | \r | |
1036 | typedef struct {\r | |
1037 | char *dnam[AUTO_MAXC];\r | |
1038 | int32 numc;\r | |
1039 | int32 numv;\r | |
1040 | uint32 amod;\r | |
1041 | uint32 vmod;\r | |
1042 | uint32 fixa[AUTO_MAXC];\r | |
1043 | uint32 fixv[AUTO_MAXC];\r | |
1044 | } AUTO_CON;\r | |
1045 | \r | |
1046 | AUTO_CON auto_tab[] = {\r | |
1047 | { { NULL }, 1, 2, 0, 8, { 0 } }, /* DLV11J - fx CSRs */\r | |
1048 | { { NULL }, 1, 2, 8, 8 }, /* DJ11 */\r | |
1049 | { { NULL }, 1, 2, 16, 8 }, /* DH11 */\r | |
1050 | { { NULL }, 1, 2, 8, 8 }, /* DQ11 */\r | |
1051 | { { NULL }, 1, 2, 8, 8 }, /* DU11 */\r | |
1052 | { { NULL }, 1, 2, 8, 8 }, /* DUP11 */\r | |
1053 | { { NULL }, 10, 2, 8, 8 }, /* LK11A */\r | |
1054 | { { NULL }, 1, 2, 8, 8 }, /* DMC11 */\r | |
1055 | { { "DZ" }, DZ_MUXES, 2, 8, 8 }, /* DZ11 */\r | |
1056 | { { NULL }, 1, 2, 8, 8 }, /* KMC11 */\r | |
1057 | { { NULL }, 1, 2, 8, 8 }, /* LPP11 */\r | |
1058 | { { NULL }, 1, 2, 8, 8 }, /* VMV21 */\r | |
1059 | { { NULL }, 1, 2, 16, 8 }, /* VMV31 */\r | |
1060 | { { NULL }, 1, 2, 8, 8 }, /* DWR70 */\r | |
1061 | { { "RL", "RLB" }, 1, 1, 8, 4, {IOBA_RL}, {VEC_RL} }, /* RL11 */\r | |
1062 | { { "TS", "TSB", "TSC", "TSD" }, 1, 1, 0, 4, /* TS11 */\r | |
1063 | {IOBA_TS, IOBA_TS + 4, IOBA_TS + 8, IOBA_TS + 12},\r | |
1064 | {VEC_TS} },\r | |
1065 | { { NULL }, 1, 2, 16, 8 }, /* LPA11K */\r | |
1066 | { { NULL }, 1, 2, 8, 8 }, /* KW11C */\r | |
1067 | { { NULL }, 1, 1, 8, 8 }, /* reserved */\r | |
1068 | { { "RX", "RY" }, 1, 1, 8, 4, {IOBA_RX} , {VEC_RX} }, /* RX11/RX211 */\r | |
1069 | { { NULL }, 1, 1, 8, 4 }, /* DR11W */\r | |
1070 | { { NULL }, 1, 1, 8, 4, { 0, 0 }, { 0 } }, /* DR11B - fx CSRs,vec */\r | |
1071 | { { NULL }, 1, 2, 8, 8 }, /* DMP11 */\r | |
1072 | { { NULL }, 1, 2, 8, 8 }, /* DPV11 */\r | |
1073 | { { NULL }, 1, 2, 8, 8 }, /* ISB11 */\r | |
1074 | { { NULL }, 1, 2, 16, 8 }, /* DMV11 */\r | |
1075 | { { "XU", "XUB" }, 1, 1, 8, 4, {IOBA_XU}, {VEC_XU} }, /* DEUNA */\r | |
1076 | { { "XQ", "XQB" }, 1, 1, 0, 4, /* DEQNA */\r | |
1077 | {IOBA_XQ,IOBA_XQB}, {VEC_XQ} },\r | |
1078 | { { "RQ", "RQB", "RQC", "RQD" }, 1, -1, 4, 4, /* RQDX3 */\r | |
1079 | {IOBA_RQ}, {VEC_RQ} },\r | |
1080 | { { NULL }, 1, 8, 32, 4 }, /* DMF32 */\r | |
1081 | { { NULL }, 1, 2, 16, 8 }, /* KMS11 */\r | |
1082 | { { NULL }, 1, 1, 16, 4 }, /* VS100 */\r | |
1083 | { { "TQ", "TQB" }, 1, -1, 4, 4, {IOBA_TQ}, {VEC_TQ} }, /* TQK50 */\r | |
1084 | { { NULL }, 1, 2, 16, 8 }, /* KMV11 */\r | |
1085 | { { "VH" }, VH_MUXES, 2, 16, 8 }, /* DHU11/DHQ11 */\r | |
1086 | { { NULL }, 1, 6, 32, 4 }, /* DMZ32 */\r | |
1087 | { { NULL }, 1, 6, 32, 4 }, /* CP132 */\r | |
1088 | { { NULL }, 1, 2, 64, 8, { 0 } }, /* QVSS - fx CSR */\r | |
1089 | { { NULL }, 1, 1, 8, 4 }, /* VS31 */\r | |
1090 | { { NULL }, 1, 1, 0, 4, { 0 } }, /* LNV11 - fx CSR */\r | |
1091 | { { NULL }, 1, 1, 16, 4 }, /* LNV21/QPSS */\r | |
1092 | { { NULL }, 1, 1, 8, 4, { 0 } }, /* QTA - fx CSR */\r | |
1093 | { { NULL }, 1, 1, 8, 4 }, /* DSV11 */\r | |
1094 | { { NULL }, 1, 2, 8, 8 }, /* CSAM */\r | |
1095 | { { NULL }, 1, 2, 8, 8 }, /* ADV11C */\r | |
1096 | { { NULL }, 1, 0, 8, 0 }, /* AAV11C */\r | |
1097 | { { NULL }, 1, 2, 8, 8, { 0 }, { 0 } }, /* AXV11C - fx CSR,vec */\r | |
1098 | { { NULL }, 1, 2, 4, 8, { 0 } }, /* KWV11C - fx CSR */\r | |
1099 | { { NULL }, 1, 2, 8, 8, { 0 } }, /* ADV11D - fx CSR */\r | |
1100 | { { NULL }, 1, 2, 8, 8, { 0 } }, /* AAV11D - fx CSR */\r | |
1101 | { { "QDSS" }, 1, 3, 0, 16, {IOBA_QDSS} }, /* QDSS - fx CSR */\r | |
1102 | { { NULL }, -1 } /* end table */\r | |
1103 | };\r | |
1104 | \r | |
1105 | t_stat auto_config (char *name, int32 nctrl)\r | |
1106 | {\r | |
1107 | uint32 csr = IOPAGEBASE + AUTO_CSRBASE;\r | |
1108 | uint32 vec = VEC_Q + AUTO_VECBASE;\r | |
1109 | AUTO_CON *autp;\r | |
1110 | DEVICE *dptr;\r | |
1111 | DIB *dibp;\r | |
1112 | uint32 j, k, vmask, amask;\r | |
1113 | \r | |
1114 | if (autcon_enb == 0) return SCPE_OK; /* enabled? */\r | |
1115 | if (name) { /* updating? */\r | |
1116 | if (nctrl < 0) return SCPE_ARG;\r | |
1117 | for (autp = auto_tab; autp->numc >= 0; autp++) {\r | |
1118 | for (j = 0; (j < AUTO_MAXC) && autp->dnam[j]; j++) {\r | |
1119 | if (strcmp (name, autp->dnam[j]) == 0)\r | |
1120 | autp->numc = nctrl;\r | |
1121 | }\r | |
1122 | }\r | |
1123 | }\r | |
1124 | for (autp = auto_tab; autp->numc >= 0; autp++) { /* loop thru table */\r | |
1125 | if (autp->amod) { /* floating csr? */\r | |
1126 | amask = autp->amod - 1;\r | |
1127 | csr = (csr + amask) & ~amask; /* align csr */\r | |
1128 | }\r | |
1129 | for (j = k = 0; (j < AUTO_MAXC) && autp->dnam[j]; j++) {\r | |
1130 | dptr = find_dev (autp->dnam[j]); /* find ctrl */\r | |
1131 | if ((dptr == NULL) || (dptr->flags & DEV_DIS) ||\r | |
1132 | !(dptr->flags & DEV_FLTA)) continue; /* enabled, floating? */\r | |
1133 | dibp = (DIB *) dptr->ctxt; /* get DIB */\r | |
1134 | if (dibp == NULL) return SCPE_IERR; /* not there??? */\r | |
1135 | if (autp->amod) { /* dyn csr needed? */\r | |
1136 | if (autp->fixa[k]) /* fixed csr avail? */\r | |
1137 | dibp->ba = autp->fixa[k]; /* use it */\r | |
1138 | else { /* no fixed left */\r | |
1139 | dibp->ba = csr; /* set CSR */\r | |
1140 | csr += (autp->numc * autp->amod); /* next CSR */\r | |
1141 | } /* end else */\r | |
1142 | } /* end if dyn csr */\r | |
1143 | if (autp->numv && autp->vmod) { /* dyn vec needed? */\r | |
1144 | uint32 numv = abs (autp->numv); /* get num vec */\r | |
1145 | if (autp->fixv[k]) { /* fixed vec avail? */\r | |
1146 | if (autp->numv > 0)\r | |
1147 | dibp->vec = autp->fixv[k]; /* use it */\r | |
1148 | }\r | |
1149 | else { /* no fixed left */\r | |
1150 | vmask = autp->vmod - 1;\r | |
1151 | vec = (vec + vmask) & ~vmask; /* align vector */\r | |
1152 | if (autp->numv > 0)\r | |
1153 | dibp->vec = vec; /* set vector */\r | |
1154 | vec += (autp->numc * numv * 4);\r | |
1155 | } /* end else */\r | |
1156 | } /* end if dyn vec */\r | |
1157 | k++; /* next instance */\r | |
1158 | } /* end for j */\r | |
1159 | if (autp->amod) csr = csr + 2; /* flt CSR? gap */\r | |
1160 | } /* end for i */\r | |
1161 | return SCPE_OK;\r | |
1162 | }\r |