1 /* id_io.c: Interdata CPU-independent I/O routines
3 Copyright (c) 2001-2006, Robert M. Supnik
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:
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
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.
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.
26 30-Mar-06 RMS Fixed bug, GO preserves EXA and SSTA (found by Davis Johnson)
27 21-Jun-03 RMS Changed subroutine argument for ARM compiler conflict
29 Interdata I/O devices are defined by a device information block:
31 dno base device number
32 sch selector channel, -1 if none
33 irq interrupt request flag
34 tplte device number template, NULL if one device number
35 iot I/O processing routine
36 ini initialization routine
38 Interdata I/O uses the following interconnected tables:
40 dev_tab[dev] Indexed by device number, points to the I/O instruction
41 processing routine for the device.
43 sch_tab[dev] Indexed by device number, if non-zero, the number + 1
44 of the selector channel used by the device.
46 int_req[level] Indexed by interrupt level, device interrupt flags.
48 int_enb[level] Indexed by interrupt level, device interrupt enable flags.
50 int_tab[idx] Indexed by ((interrupt level * 32) + interrupt number),
51 maps bit positions in int_req to device numbers.
56 /* Selector channel */
58 #define SCHC_EXA 0x40 /* read ext addr */
59 #define SCHC_RD 0x20 /* read */
60 #define SCHC_GO 0x10 /* go */
61 #define SCHC_STOP 0x08 /* stop */
62 #define SCHC_SSTA 0x04 /* sel ch status */
63 #define SCHC_EXM 0x03 /* ext mem */
65 extern uint32 int_req
[INTSZ
], int_enb
[INTSZ
];
66 extern uint32 (*dev_tab
[DEVNO
])(uint32 dev
, uint32 op
, uint32 datout
);
67 extern uint32 pawidth
;
70 extern DEVICE
*sim_devices
[];
72 uint32 sch_max
= 2; /* sch count */
73 uint32 sch_sa
[SCH_NUMCH
] = { 0 }; /* start addr */
74 uint32 sch_ea
[SCH_NUMCH
] = { 0 }; /* end addr */
75 uint8 sch_sdv
[SCH_NUMCH
] = { 0 }; /* device */
76 uint8 sch_cmd
[SCH_NUMCH
] = { 0 }; /* command */
77 uint8 sch_rdp
[SCH_NUMCH
] = { 0 }; /* read ptr */
78 uint8 sch_wdc
[SCH_NUMCH
] = { 0 }; /* write ctr */
79 uint32 sch_tab
[DEVNO
] = { 0 }; /* dev to sch map */
80 uint32 int_tab
[INTSZ
* 32] = { 0 }; /* int to dev map */
81 uint8 sch_tplte
[SCH_NUMCH
+ 1]; /* dnum template */
83 uint32
sch (uint32 dev
, uint32 op
, uint32 dat
);
84 void sch_ini (t_bool dtpl
);
85 t_stat
sch_reset (DEVICE
*dptr
);
86 t_stat
sch_set_nchan (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
);
87 t_stat
sch_show_reg (FILE *st
, UNIT
*uptr
, int32 val
, void *desc
);
89 /* Selector channel data structures
91 sch_dev channel device descriptor
92 sch_unit channel unit descriptor
93 sch_mod channel modifiers list
94 sch_reg channel register list
97 DIB sch_dib
= { d_SCH
, -1, v_SCH
, sch_tplte
, &sch
, &sch_ini
};
99 UNIT sch_unit
= { UDATA (NULL
, 0, 0) };
102 { HRDATA (CHAN
, sch_max
, 3), REG_HRO
},
103 { BRDATA (SA
, sch_sa
, 16, 20, SCH_NUMCH
) },
104 { BRDATA (EA
, sch_ea
, 16, 20, SCH_NUMCH
) },
105 { BRDATA (CMD
, sch_cmd
, 16, 8, SCH_NUMCH
) },
106 { BRDATA (DEV
, sch_sdv
, 16, 8, SCH_NUMCH
) },
107 { BRDATA (RDP
, sch_rdp
, 16, 2, SCH_NUMCH
) },
108 { BRDATA (WDC
, sch_wdc
, 16, 3, SCH_NUMCH
) },
109 { GRDATA (IREQ
, int_req
[l_SCH
], 16, SCH_NUMCH
, i_SCH
) },
110 { GRDATA (IENB
, int_enb
[l_SCH
], 16, SCH_NUMCH
, i_SCH
) },
111 { HRDATA (DEVNO
, sch_dib
.dno
, 8), REG_HRO
},
116 { MTAB_XTD
|MTAB_VDV
|MTAB_VAL
, 0, "channels", "CHANNELS",
117 &sch_set_nchan
, NULL
, &sch_reg
[0] },
118 { MTAB_XTD
| MTAB_VDV
| MTAB_NMO
, 0, "0", NULL
,
119 NULL
, &sch_show_reg
, NULL
},
120 { MTAB_XTD
| MTAB_VDV
| MTAB_NMO
, 1, "1", NULL
,
121 NULL
, &sch_show_reg
, NULL
},
122 { MTAB_XTD
| MTAB_VDV
| MTAB_NMO
, 2, "2", NULL
,
123 NULL
, &sch_show_reg
, NULL
},
124 { MTAB_XTD
| MTAB_VDV
| MTAB_NMO
, 3, "3", NULL
,
125 NULL
, &sch_show_reg
, NULL
},
126 { MTAB_XTD
|MTAB_VDV
, 0, "DEVNO", "DEVNO",
127 &set_dev
, &show_dev
, &sch_dib
},
132 "SELCH", &sch_unit
, sch_reg
, sch_mod
,
134 NULL
, NULL
, &sch_reset
,
139 /* (Extended) selector channel
141 There are really three different selector channels:
142 - 16b selector channel (max 4B of data)
143 - 18b selector channel (max 4B of data)
144 - 20b selector channel (max 6B of data)
145 The algorithm for loading the start and end addresses is taken
146 from the maintenance manual for the Extended Selector Channel.
149 #define SCH_EXR(ch) ((sch_cmd[ch] & SCHC_EXA) && (pawidth == PAWIDTH32))
151 uint32
sch (uint32 dev
, uint32 op
, uint32 dat
)
153 uint32 t
, bank
, sdv
, ch
= dev
- sch_dib
.dno
;
155 switch (op
) { /* case IO op */
157 case IO_ADR
: /* select */
158 return BY
; /* byte only */
160 case IO_RD
: /* read data */
161 t
= (sch_sa
[ch
] >> (sch_rdp
[ch
] * 8)) & DMASK8
; /* get sa byte */
162 if (sch_rdp
[ch
] == 0) sch_rdp
[ch
] = /* wrap? */
164 else sch_rdp
[ch
] = sch_rdp
[ch
] - 1; /* dec byte ptr */
167 case IO_WD
: /* write data */
168 if (pawidth
!= PAWIDTH32
) { /* 16b? max 4 */
169 if (sch_wdc
[ch
] >= 4) break; /* stop at 4 */
170 sch_sa
[ch
] = ((sch_sa
[ch
] << 8) | /* ripple ea to sa */
171 (sch_ea
[ch
] >> 8)) & DMASK16
;
172 sch_ea
[ch
] = ((sch_ea
[ch
] << 8) | /* ripple ea low */
173 dat
) & DMASK16
; /* insert byte */
175 else { /* 32b? max 6 */
176 if (sch_wdc
[ch
] >= 6) break; /* stop at 6 */
177 if (sch_wdc
[ch
] != 5) { /* if not last */
178 sch_sa
[ch
] = ((sch_sa
[ch
] << 8) | /* ripple ea<15:8> to sa */
179 ((sch_ea
[ch
] >> 8) & DMASK8
)) & PAMASK32
;
180 sch_ea
[ch
] = /* ripple ea<7:0> */
181 (((sch_ea
[ch
] & DMASK8
) << 8) | dat
) & PAMASK32
;
183 else sch_ea
[ch
] = ((sch_ea
[ch
] << 8) | dat
) & PAMASK32
;
185 sch_wdc
[ch
] = sch_wdc
[ch
] + 1; /* adv sequencer */
188 case IO_SS
: /* status */
189 if (sch_cmd
[ch
] & SCHC_GO
) return STA_BSY
; /* test busy */
190 if (sch_cmd
[ch
] & SCHC_SSTA
) return 0; /* test sch sta */
192 sdv
= sch_sdv
[ch
]; /* get dev */
193 if (dev_tab
[sdv
] == 0) return CC_V
; /* not there? */
194 dev_tab
[sdv
] (sdv
, IO_ADR
, 0); /* select dev */
195 t
= dev_tab
[sdv
] (sdv
, IO_SS
, 0); /* get status */
196 return t
& ~STA_BSY
; /* clr busy */
199 case IO_OC
: /* command */
200 bank
= 0; /* assume no bank */
201 if (pawidth
!= PAWIDTH32
) { /* 16b/18b proc? */
202 dat
= dat
& ~(SCHC_EXA
| SCHC_SSTA
); /* clr ext func */
203 if (pawidth
== PAWIDTH16E
) /* 18b proc? */
204 bank
= (dat
& SCHC_EXM
) << 16;
206 if (dat
& SCHC_STOP
) { /* stop? */
207 sch_cmd
[ch
] = dat
& (SCHC_EXA
| SCHC_SSTA
); /* clr go */
208 CLR_INT (v_SCH
+ ch
); /* clr intr */
209 sch_rdp
[ch
] = SCH_EXR (ch
)? 2: 1; /* init sequencers */
212 else if (dat
& SCHC_GO
) { /* go? */
213 sch_cmd
[ch
] = dat
& (SCHC_EXA
| SCHC_SSTA
| SCHC_GO
| SCHC_RD
);
214 if (sch_wdc
[ch
] <= 4) { /* 4 bytes? */
215 sch_sa
[ch
] = (sch_sa
[ch
] & PAMASK16
) | bank
; /* 16b addr */
216 sch_ea
[ch
] = (sch_ea
[ch
] & PAMASK16
) | bank
;
218 sch_sa
[ch
] = sch_sa
[ch
] & ~1;
219 if (sch_ea
[ch
] <= sch_sa
[ch
]) /* wrap? */
220 sch_ea
[ch
] = sch_ea
[ch
] | /* modify end addr */
221 ((pawidth
== PAWIDTH32
)? PAMASK32
: PAMASK16
);
229 /* CPU call to test if channel blocks access to device */
231 t_bool
sch_blk (uint32 dev
)
233 uint32 ch
= sch_tab
[dev
] - 1;
235 if ((ch
< sch_max
) && (sch_cmd
[ch
] & SCHC_GO
)) return TRUE
;
239 /* Device call to 'remember' last dev on channel */
241 void sch_adr (uint32 ch
, uint32 dev
)
243 if (ch
< sch_max
) sch_sdv
[ch
] = dev
;
247 /* Device call to see if selector channel is active for device */
249 t_bool
sch_actv (uint32 ch
, uint32 dev
)
251 if ((ch
< sch_max
) && /* chan valid, */
252 (sch_cmd
[ch
] & SCHC_GO
) && /* on, and */
253 (sch_sdv
[ch
] == dev
)) return TRUE
; /* set for dev? */
254 return FALSE
; /* no */
257 /* Device call to read a block of memory */
259 uint32
sch_rdmem (uint32 ch
, uint8
*buf
, uint32 cnt
)
261 uint32 addr
, end
, xfr
, inc
;
263 if ((ch
>= sch_max
) || ((sch_cmd
[ch
] & SCHC_GO
) == 0)) return 0;
264 addr
= sch_sa
[ch
]; /* start */
265 end
= sch_ea
[ch
]; /* end */
266 xfr
= MIN (cnt
, end
- addr
+ 1); /* sch xfr cnt */
267 inc
= IOReadBlk (addr
, xfr
, buf
); /* read mem */
268 if ((addr
+ inc
) > end
) { /* end? */
269 SET_INT (v_SCH
+ ch
); /* interrupt */
270 sch_cmd
[ch
] &= ~(SCHC_GO
| SCHC_RD
); /* clear GO */
271 sch_sa
[ch
] = sch_sa
[ch
] + inc
- 1; /* end addr */
273 else sch_sa
[ch
] = sch_sa
[ch
] + inc
; /* next addr */
277 /* Device call to write a block of memory */
279 uint32
sch_wrmem (uint32 ch
, uint8
*buf
, uint32 cnt
)
281 uint32 addr
, end
, xfr
, inc
;
283 if ((ch
>= sch_max
) || ((sch_cmd
[ch
] & SCHC_GO
) == 0)) return 0;
284 addr
= sch_sa
[ch
]; /* start */
285 end
= sch_ea
[ch
]; /* end */
286 xfr
= MIN (cnt
, end
- addr
+ 1); /* sch xfr cnt */
287 inc
= IOWriteBlk (addr
, xfr
, buf
); /* write mem */
288 if ((addr
+ inc
) > end
) { /* end? */
289 SET_INT (v_SCH
+ ch
); /* interrupt */
290 sch_cmd
[ch
] &= ~(SCHC_GO
| SCHC_RD
); /* clear GO */
291 sch_sa
[ch
] = sch_sa
[ch
] + inc
- 1; /* end addr */
293 else sch_sa
[ch
] = sch_sa
[ch
] + inc
; /* next addr */
297 /* Device call to stop a selector channel */
299 void sch_stop (uint32 ch
)
302 SET_INT (v_SCH
+ ch
); /* interrupt */
303 sch_cmd
[ch
] &= ~(SCHC_GO
| SCHC_RD
); /* clear GO */
310 void sch_reset_ch (uint32 rst_lim
)
314 for (ch
= 0; ch
< SCH_NUMCH
; ch
++) {
316 CLR_INT (v_SCH
+ ch
);
317 SET_ENB (v_SCH
+ ch
);
318 sch_sa
[ch
] = sch_ea
[ch
] = 0;
319 sch_cmd
[ch
] = sch_sdv
[ch
] = 0;
327 t_stat
sch_reset (DEVICE
*dptr
)
329 sch_reset_ch (0); /* reset all chan */
333 /* Set number of channels */
335 t_stat
sch_set_nchan (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
)
342 if (cptr
== NULL
) return SCPE_ARG
;
343 newmax
= get_uint (cptr
, 10, SCH_NUMCH
, &r
); /* get new max */
344 if ((r
!= SCPE_OK
) || (newmax
== sch_max
)) return r
; /* err or no chg? */
345 if (newmax
== 0) return SCPE_ARG
; /* must be > 0 */
346 if (newmax
< sch_max
) { /* reducing? */
347 for (i
= 0; dptr
= sim_devices
[i
]; i
++) { /* loop thru dev */
348 dibp
= (DIB
*) dptr
->ctxt
; /* get DIB */
349 if (dibp
&& (dibp
->sch
>= (int32
) newmax
)) { /* dev using chan? */
350 printf ("Device %02X uses channel %d\n",
351 dibp
->dno
, dibp
->sch
);
352 if (sim_log
) fprintf (sim_log
, "Device %02X uses channel %d\n",
353 dibp
->dno
, dibp
->sch
);
358 sch_max
= newmax
; /* set new max */
359 sch_reset_ch (sch_max
); /* reset chan */
363 /* Show channel registers */
365 t_stat
sch_show_reg (FILE *st
, UNIT
*uptr
, int32 val
, void *desc
)
367 if (val
< 0) return SCPE_IERR
;
368 if (val
>= (int32
) sch_max
) fprintf (st
, "Channel %d disabled\n", val
);
370 fprintf (st
, "SA: %05X\n", sch_sa
[val
]);
371 fprintf (st
, "EA: %05X\n", sch_ea
[val
]);
372 fprintf (st
, "CMD: %02X\n", sch_cmd
[val
]);
373 fprintf (st
, "DEV: %02X\n", sch_sdv
[val
]);
374 fprintf (st
, "RDP: %X\n", sch_rdp
[val
]);
375 fprintf (st
, "WDC: %X\n", sch_wdc
[val
]);
380 /* Initialize template */
382 void sch_ini (t_bool dtpl
)
386 for (i
= 0; i
< sch_max
; i
++) sch_tplte
[i
] = i
;
387 sch_tplte
[sch_max
] = TPL_END
;
391 /* Evaluate interrupt */
396 extern uint32 qevent
;
398 for (i
= 0; i
< INTSZ
; i
++) {
399 if (int_req
[i
] & int_enb
[i
]) {
400 qevent
= qevent
| EV_INT
;
404 qevent
= qevent
& ~EV_INT
;
408 /* Return interrupting device */
410 uint32
int_getdev (void)
415 for (i
= t
= 0; i
< INTSZ
; i
++) { /* loop thru array */
416 if (r
= int_req
[i
] & int_enb
[i
]) { /* find nz int wd */
417 for (j
= 0; j
< 32; t
++, j
++) {
419 int_req
[i
] = int_req
[i
] & ~(1u << j
); /* clr request */
429 /* Update device interrupt status */
431 int32
int_chg (uint32 irq
, int32 dat
, int32 armdis
)
433 int32 t
= CMD_GETINT (dat
); /* get int ctrl */
435 if (t
== CMD_IENB
) { /* enable? */
439 else if (t
== CMD_IDIS
) { /* disable? */
443 if (t
== CMD_IDSA
) { /* disarm? */
451 /* Process a 2b field and return unchanged, set, clear, complement */
453 int32
io_2b (int32 val
, int32 pos
, int32 old
)
455 int32 t
= (val
>> pos
) & 3;
456 if (t
== 0) return old
;
457 if (t
== 1) return 1;
458 if (t
== 2) return 0;
462 /* Block transfer routines */
464 uint32
IOReadBlk (uint32 loc
, uint32 cnt
, uint8
*buf
)
468 if (!MEM_ADDR_OK (loc
) || (cnt
== 0)) return 0;
469 if (!MEM_ADDR_OK (loc
+ cnt
- 1)) cnt
= MEMSIZE
- loc
;
470 for (i
= 0; i
< cnt
; i
++) buf
[i
] = IOReadB (loc
+ i
);
474 uint32
IOWriteBlk (uint32 loc
, uint32 cnt
, uint8
*buf
)
478 if (!MEM_ADDR_OK (loc
) || (cnt
== 0)) return 0;
479 if (!MEM_ADDR_OK (loc
+ cnt
- 1)) cnt
= MEMSIZE
- loc
;
480 for (i
= 0; i
< cnt
; i
++) IOWriteB (loc
+ i
, buf
[i
]);
484 /* Change selector channel for a device */
486 t_stat
set_sch (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
)
493 if (cptr
== NULL
) return SCPE_ARG
;
494 if (uptr
== NULL
) return SCPE_IERR
;
495 dptr
= find_dev_from_unit (uptr
);
496 if (dptr
== NULL
) return SCPE_IERR
;
497 dibp
= (DIB
*) dptr
->ctxt
;
498 if ((dibp
== NULL
) || (dibp
->sch
< 0)) return SCPE_IERR
;
499 newch
= get_uint (cptr
, 16, sch_max
- 1, &r
); /* get new */
500 if (r
!= SCPE_OK
) return r
;
501 dibp
->sch
= newch
; /* store */
505 /* Show selector channel for a device */
507 t_stat
show_sch (FILE *st
, UNIT
*uptr
, int32 val
, void *desc
)
512 if (uptr
== NULL
) return SCPE_IERR
;
513 dptr
= find_dev_from_unit (uptr
);
514 if (dptr
== NULL
) return SCPE_IERR
;
515 dibp
= (DIB
*) dptr
->ctxt
;
516 if ((dibp
== NULL
) || (dibp
->sch
< 0)) return SCPE_IERR
;
517 fprintf (st
, "selch=%X", dibp
->sch
);
521 /* Change device number for a device */
523 t_stat
set_dev (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
)
530 if (cptr
== NULL
) return SCPE_ARG
;
531 if (uptr
== NULL
) return SCPE_IERR
;
532 dptr
= find_dev_from_unit (uptr
);
533 if (dptr
== NULL
) return SCPE_IERR
;
534 dibp
= (DIB
*) dptr
->ctxt
;
535 if (dibp
== NULL
) return SCPE_IERR
;
536 newdev
= get_uint (cptr
, 16, DEV_MAX
, &r
); /* get new */
537 if ((r
!= SCPE_OK
) || (newdev
== dibp
->dno
)) return r
;
538 if (newdev
== 0) return SCPE_ARG
; /* must be > 0 */
539 dibp
->dno
= newdev
; /* store */
543 /* Show device number for a device */
545 t_stat
show_dev (FILE *st
, UNIT
*uptr
, int32 val
, void *desc
)
550 if (uptr
== NULL
) return SCPE_IERR
;
551 dptr
= find_dev_from_unit (uptr
);
552 if (dptr
== NULL
) return SCPE_IERR
;
553 dibp
= (DIB
*) dptr
->ctxt
;
554 if ((dibp
== NULL
) || (dibp
->dno
== 0)) return SCPE_IERR
;
555 fprintf (st
, "devno=%02X", dibp
->dno
);
559 /* Init device tables */
561 t_bool
devtab_init (void)
565 uint32 i
, j
, dno
, dmsk
, doff
, t
, dmap
[DEVNO
/ 32];
566 uint8
*tplte
, dflt_tplte
[] = { 0, TPL_END
};
568 /* Clear tables, device map */
570 for (i
= 0; i
< DEVNO
; i
++) {
574 for (i
= 0; i
< (INTSZ
* 32); i
++) int_tab
[i
] = 0;
575 for (i
= 0; i
< (DEVNO
/ 32); i
++) dmap
[i
] = 0;
577 /* Test each device for conflict; add to map; init tables */
579 for (i
= 0; dptr
= sim_devices
[i
]; i
++) { /* loop thru devices */
580 dibp
= (DIB
*) dptr
->ctxt
; /* get DIB */
581 if ((dibp
== NULL
) || (dptr
->flags
& DEV_DIS
)) continue; /* exist, enabled? */
582 dno
= dibp
->dno
; /* get device num */
583 if (dibp
->ini
) dibp
->ini (TRUE
); /* gen dno template */
584 tplte
= dibp
->tplte
; /* get template */
585 if (tplte
== NULL
) tplte
= dflt_tplte
; /* none? use default */
586 for ( ; *tplte
!= TPL_END
; tplte
++) { /* loop thru template */
587 t
= (dno
+ *tplte
) & DEV_MAX
; /* loop thru template */
588 dmsk
= 1u << (t
& 0x1F); /* bit to test */
589 doff
= t
/ 32; /* word to test */
590 if (dmap
[doff
] & dmsk
) { /* in use? */
591 printf ("Device number conflict, devno = %02X\n", t
);
592 if (sim_log
) fprintf (sim_log
,
593 "Device number conflict, devno = %02X\n", t
);
596 dmap
[doff
] = dmap
[doff
] | dmsk
;
597 if (dibp
->sch
>= 0) sch_tab
[t
] = dibp
->sch
+ 1;
598 dev_tab
[t
] = dibp
->iot
;
600 if (dibp
->ini
) dibp
->ini (FALSE
); /* gen int template */
601 tplte
= dibp
->tplte
; /* get template */
602 if (tplte
== NULL
) tplte
= dflt_tplte
; /* none? use default */
603 for (j
= dibp
->irq
; *tplte
!= TPL_END
; j
++, tplte
++)
604 int_tab
[j
] = (dno
+ *tplte
) & DEV_MAX
;