1 /* pdp11_io.c: PDP-11 I/O simulator
2
3 Copyright (c) 1993-2012, Robert M Supnik
4
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 Except as contained in this notice, the name of Robert M Supnik shall not be
23 used in advertising or otherwise to promote the sale, use or other dealings
24 in this Software without prior written authorization from Robert M Supnik.
25
26 27-Mar-12 RMS Fixed order of int_internal (Jordi Guillaumes i Pons)
27 19-Mar-12 RMS Fixed declaration of cpu_opt (Mark Pizzolato)
28 12-Dec-11 RMS Fixed Qbus interrupts to treat all IO devices as BR4
29 19-Nov-08 RMS Moved I/O support routines to I/O library
30 16-May-08 RMS Added multiple DC11 support
31 Renamed DL11 in autoconfigure
32 02-Feb-08 RMS Fixed DMA memory address limit test (John Dundas)
33 06-Jul-06 RMS Added multiple KL11/DL11 support
34 15-Oct-05 RMS Fixed bug in autoconfiguration (missing XU)
35 25-Jul-05 RMS Revised autoconfiguration algorithm and interface
36 30-Sep-04 RMS Revised Unibus interface
37 28-May-04 RMS Revised I/O dispatching (John Dundas)
38 25-Jan-04 RMS Removed local debug logging support
39 21-Dec-03 RMS Fixed bug in autoconfigure vector assignment; added controls
40 21-Nov-03 RMS Added check for interrupt slot conflict (Dave Hittner)
41 12-Mar-03 RMS Added logical name support
42 08-Oct-02 RMS Trimmed I/O bus addresses
43 Added support for dynamic tables
44 Added show I/O space, autoconfigure routines
45 12-Sep-02 RMS Added support for TMSCP, KW11P, RX211
46 26-Jan-02 RMS Revised for multiple DZ's
47 06-Jan-02 RMS Revised I/O access, enable/disable support
48 11-Dec-01 RMS Moved interrupt debug code
49 08-Nov-01 RMS Cloned from cpu sources
50 */
51
52 #include "pdp11_defs.h"
53
54 extern uint16 *M;
55 extern int32 int_req[IPL_HLVL];
56 extern int32 ub_map[UBM_LNT_LW];
57 extern uint32 cpu_opt;
58 extern int32 cpu_bme;
59 extern int32 trap_req, ipl;
60 extern int32 cpu_log;
61 extern int32 autcon_enb;
62 extern int32 uba_last;
63 extern FILE *sim_log;
64 extern DEVICE *sim_devices[], cpu_dev;
65 extern t_addr cpu_memsize;
66
67 int32 calc_ints (int32 nipl, int32 trq);
68
69 extern t_stat cpu_build_dib (void);
70 extern void init_mbus_tab (void);
71 extern t_stat build_mbus_tab (DEVICE *dptr, DIB *dibp);
72
73 /* I/O data structures */
74
75 t_stat (*iodispR[IOPAGESIZE >> 1])(int32 *dat, int32 ad, int32 md);
76 t_stat (*iodispW[IOPAGESIZE >> 1])(int32 dat, int32 ad, int32 md);
77
78 int32 int_vec[IPL_HLVL][32]; /* int req to vector */
79 int32 (*int_ack[IPL_HLVL][32])(void); /* int ack routines */
80
81 static const int32 pirq_bit[7] = {
82 INT_V_PIR1, INT_V_PIR2, INT_V_PIR3, INT_V_PIR4,
83 INT_V_PIR5, INT_V_PIR6, INT_V_PIR7
84 };
85
86 static const int32 int_internal[IPL_HLVL] = {
87 0, INT_INTERNAL1, INT_INTERNAL2, INT_INTERNAL3,
88 INT_INTERNAL4, INT_INTERNAL5, INT_INTERNAL6, INT_INTERNAL7
89 };
90
91 /* I/O page lookup and linkage routines
92
93 Inputs:
94 *data = pointer to data to read, if READ
95 data = data to store, if WRITE or WRITEB
96 pa = address
97 access = READ, WRITE, or WRITEB
98 Outputs:
99 status = SCPE_OK or SCPE_NXM
100 */
101
iopageR(int32 * data,uint32 pa,int32 access)102 t_stat iopageR (int32 *data, uint32 pa, int32 access)
103 {
104 int32 idx;
105 t_stat stat;
106
107 idx = (pa & IOPAGEMASK) >> 1;
108 if (iodispR[idx]) {
109 stat = iodispR[idx] (data, pa, access);
110 trap_req = calc_ints (ipl, trap_req);
111 return stat;
112 }
113 return SCPE_NXM;
114 }
115
iopageW(int32 data,uint32 pa,int32 access)116 t_stat iopageW (int32 data, uint32 pa, int32 access)
117 {
118 int32 idx;
119 t_stat stat;
120
121 idx = (pa & IOPAGEMASK) >> 1;
122 if (iodispW[idx]) {
123 stat = iodispW[idx] (data, pa, access);
124 trap_req = calc_ints (ipl, trap_req);
125 return stat;
126 }
127 return SCPE_NXM;
128 }
129
130 /* Calculate interrupt outstanding
131 In a Qbus system, all device interrupts are treated as BR4 */
132
calc_ints(int32 nipl,int32 trq)133 int32 calc_ints (int32 nipl, int32 trq)
134 {
135 int32 i, t;
136 t_bool all_int = (UNIBUS || (nipl < IPL_HMIN));
137
138 for (i = IPL_HLVL - 1; i > nipl; i--) {
139 t = all_int? int_req[i]: (int_req[i] & int_internal[i]);
140 if (t)
141 return (trq | TRAP_INT);
142 }
143 return (trq & ~TRAP_INT);
144 }
145
146 /* Find vector for highest priority interrupt
147 In a Qbus system, all device interrupts are treated as BR4 */
148
get_vector(int32 nipl)149 int32 get_vector (int32 nipl)
150 {
151 int32 i, j, t, vec;
152 t_bool all_int = (UNIBUS || (nipl < IPL_HMIN));
153
154 for (i = IPL_HLVL - 1; i > nipl; i--) { /* loop thru lvls */
155 t = all_int? int_req[i]: (int_req[i] & int_internal[i]);
156 for (j = 0; t && (j < 32); j++) { /* srch level */
157 if ((t >> j) & 1) { /* irq found? */
158 int_req[i] = int_req[i] & ~(1u << j); /* clr irq */
159 if (int_ack[i][j])
160 vec = int_ack[i][j]();
161 else vec = int_vec[i][j];
162 return vec; /* return vector */
163 } /* end if t */
164 } /* end for j */
165 } /* end for i */
166 return 0;
167 }
168
169 /* Read and write Unibus map registers
170
171 In any even/odd pair
172 even = low 16b, bit <0> clear
173 odd = high 6b
174
175 The Unibus map is stored as an array of longwords.
176 These routines are only reachable if a Unibus map is configured.
177 */
178
ubm_rd(int32 * data,int32 addr,int32 access)179 t_stat ubm_rd (int32 *data, int32 addr, int32 access)
180 {
181 int32 pg = (addr >> 2) & UBM_M_PN;
182
183 *data = (addr & 2)? ((ub_map[pg] >> 16) & 077):
184 (ub_map[pg] & 0177776);
185 return SCPE_OK;
186 }
187
ubm_wr(int32 data,int32 addr,int32 access)188 t_stat ubm_wr (int32 data, int32 addr, int32 access)
189 {
190 int32 sc, pg = (addr >> 2) & UBM_M_PN;
191
192 if (access == WRITEB) {
193 sc = (addr & 3) << 3;
194 ub_map[pg] = (ub_map[pg] & ~(0377 << sc)) |
195 ((data & 0377) << sc);
196 }
197 else {
198 sc = (addr & 2) << 3;
199 ub_map[pg] = (ub_map[pg] & ~(0177777 << sc)) |
200 ((data & 0177777) << sc);
201 }
202 ub_map[pg] = ub_map[pg] & 017777776;
203 return SCPE_OK;
204 }
205
206 /* Mapped memory access routines for DMA devices */
207
208 #define BUSMASK ((UNIBUS)? UNIMASK: PAMASK)
209
210 /* Map I/O address to memory address - caller checks cpu_bme */
211
Map_Addr(uint32 ba)212 uint32 Map_Addr (uint32 ba)
213 {
214 int32 pg = UBM_GETPN (ba); /* map entry */
215 int32 off = UBM_GETOFF (ba); /* offset */
216
217 if (pg != UBM_M_PN) /* last page? */
218 uba_last = (ub_map[pg] + off) & PAMASK; /* no, use map */
219 else uba_last = (IOPAGEBASE + off) & PAMASK; /* yes, use fixed */
220 return uba_last;
221 }
222
223 /* I/O buffer routines, aligned access
224
225 Map_ReadB - fetch byte buffer from memory
226 Map_ReadW - fetch word buffer from memory
227 Map_WriteB - store byte buffer into memory
228 Map_WriteW - store word buffer into memory
229
230 These routines are used only for Unibus and Qbus devices.
231 Massbus devices have their own IO routines. As a result,
232 the historic 'map' parameter is no longer needed.
233
234 - In a U18 configuration, the map is always disabled.
235 Device addresses are trimmed to 18b.
236 - In a U22 configuration, the map is always configured
237 (although it may be disabled). Device addresses are
238 trimmed to 18b.
239 - In a Qbus configuration, the map is always disabled.
240 Device addresses are trimmed to 22b.
241 */
242
Map_ReadB(uint32 ba,int32 bc,uint8 * buf)243 int32 Map_ReadB (uint32 ba, int32 bc, uint8 *buf)
244 {
245 uint32 alim, lim, ma;
246
247 ba = ba & BUSMASK; /* trim address */
248 lim = ba + bc;
249 if (cpu_bme) { /* map enabled? */
250 for ( ; ba < lim; ba++) { /* by bytes */
251 ma = Map_Addr (ba); /* map addr */
252 if (!ADDR_IS_MEM (ma)) /* NXM? err */
253 return (lim - ba);
254 if (ma & 1) /* get byte */
255 *buf++ = (M[ma >> 1] >> 8) & 0377;
256 else *buf++ = M[ma >> 1] & 0377;
257 }
258 return 0;
259 }
260 else { /* physical */
261 if (ADDR_IS_MEM (lim)) /* end ok? */
262 alim = lim;
263 else if (ADDR_IS_MEM (ba)) /* no, strt ok? */
264 alim = cpu_memsize;
265 else return bc; /* no, err */
266 for ( ; ba < alim; ba++) { /* by bytes */
267 if (ba & 1)
268 *buf++ = (M[ba >> 1] >> 8) & 0377; /* get byte */
269 else *buf++ = M[ba >> 1] & 0377;
270 }
271 return (lim - alim);
272 }
273 }
274
Map_ReadW(uint32 ba,int32 bc,uint16 * buf)275 int32 Map_ReadW (uint32 ba, int32 bc, uint16 *buf)
276 {
277 uint32 alim, lim, ma;
278
279 ba = (ba & BUSMASK) & ~01; /* trim, align addr */
280 lim = ba + (bc & ~01);
281 if (cpu_bme) { /* map enabled? */
282 for (; ba < lim; ba = ba + 2) { /* by words */
283 ma = Map_Addr (ba); /* map addr */
284 if (!ADDR_IS_MEM (ma)) /* NXM? err */
285 return (lim - ba);
286 *buf++ = M[ma >> 1];
287 }
288 return 0;
289 }
290 else { /* physical */
291 if (ADDR_IS_MEM (lim)) /* end ok? */
292 alim = lim;
293 else if (ADDR_IS_MEM (ba)) /* no, strt ok? */
294 alim = cpu_memsize;
295 else return bc; /* no, err */
296 for ( ; ba < alim; ba = ba + 2) { /* by words */
297 *buf++ = M[ba >> 1];
298 }
299 return (lim - alim);
300 }
301 }
302
Map_WriteB(uint32 ba,int32 bc,uint8 * buf)303 int32 Map_WriteB (uint32 ba, int32 bc, uint8 *buf)
304 {
305 uint32 alim, lim, ma;
306
307 ba = ba & BUSMASK; /* trim address */
308 lim = ba + bc;
309 if (cpu_bme) { /* map enabled? */
310 for ( ; ba < lim; ba++) { /* by bytes */
311 ma = Map_Addr (ba); /* map addr */
312 if (!ADDR_IS_MEM (ma)) /* NXM? err */
313 return (lim - ba);
314 if (ma & 1) M[ma >> 1] = (M[ma >> 1] & 0377) |
315 ((uint16) *buf++ << 8);
316 else M[ma >> 1] = (M[ma >> 1] & ~0377) | *buf++;
317 }
318 return 0;
319 }
320 else { /* physical */
321 if (ADDR_IS_MEM (lim)) /* end ok? */
322 alim = lim;
323 else if (ADDR_IS_MEM (ba)) /* no, strt ok? */
324 alim = cpu_memsize;
325 else return bc; /* no, err */
326 for ( ; ba < alim; ba++) { /* by bytes */
327 if (ba & 1)
328 M[ba >> 1] = (M[ba >> 1] & 0377) | ((uint16) *buf++ << 8);
329 else M[ba >> 1] = (M[ba >> 1] & ~0377) | *buf++;
330 }
331 return (lim - alim);
332 }
333 }
334
Map_WriteW(uint32 ba,int32 bc,uint16 * buf)335 int32 Map_WriteW (uint32 ba, int32 bc, uint16 *buf)
336 {
337 uint32 alim, lim, ma;
338
339 ba = (ba & BUSMASK) & ~01; /* trim, align addr */
340 lim = ba + (bc & ~01);
341 if (cpu_bme) { /* map enabled? */
342 for (; ba < lim; ba = ba + 2) { /* by words */
343 ma = Map_Addr (ba); /* map addr */
344 if (!ADDR_IS_MEM (ma)) /* NXM? err */
345 return (lim - ba);
346 M[ma >> 1] = *buf++;
347 }
348 return 0;
349 }
350 else { /* physical */
351 if (ADDR_IS_MEM (lim)) /* end ok? */
352 alim = lim;
353 else if (ADDR_IS_MEM (ba)) /* no, strt ok? */
354 alim = cpu_memsize;
355 else return bc; /* no, err */
356 for ( ; ba < alim; ba = ba + 2) { /* by words */
357 M[ba >> 1] = *buf++;
358 }
359 return (lim - alim);
360 }
361 }
362
363 /* Build tables from device list */
364
build_dib_tab(void)365 t_stat build_dib_tab (void)
366 {
367 int32 i;
368 DEVICE *dptr;
369 DIB *dibp;
370 t_stat r;
371
372 init_ubus_tab (); /* init Unibus tables */
373 init_mbus_tab (); /* init Massbus tables */
374 for (i = 0; i < 7; i++) /* seed PIRQ intr */
375 int_vec[i + 1][pirq_bit[i]] = VEC_PIRQ;
376 if ((r = cpu_build_dib ())) /* build CPU entries */
377 return r;
378 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */
379 dibp = (DIB *) dptr->ctxt; /* get DIB */
380 if (dibp && !(dptr->flags & DEV_DIS)) { /* defined, enabled? */
381 if (dptr->flags & DEV_MBUS) { /* Massbus? */
382 if ((r = build_mbus_tab (dptr, dibp))) /* add to Mbus tab */
383 return r;
384 }
385 else { /* no, Unibus */
386 if ((r = build_ubus_tab (dptr, dibp))) /* add to Unibus tab */
387 return r;
388 }
389 } /* end if enabled */
390 } /* end for */
391 return SCPE_OK;
392 }
393