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