1 /* sds_io.c: SDS 940 I/O simulator
3 Copyright (c) 2001-2005, 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.
31 #define CHD_INT 040 /* int on chain */
32 #define CHD_PAGE 037 /* new page # */
36 #define CHI_V_WC 14 /* word count */
37 #define CHI_M_WC 01777
38 #define CHI_GETWC(x) (((x) >> CHI_V_WC) & CHI_M_WC)
39 #define CHI_V_MA 0 /* mem address */
40 #define CHI_M_MA 037777
41 #define CHI_GETMA(x) (((x) >> CHI_V_MA) & CHI_M_MA)
43 /* System interrupt POT */
45 #define SYI_V_GRP 18 /* group */
47 #define SYI_GETGRP(x) (((x) >> SYI_V_GRP) & SYI_M_GRP)
48 #define SYI_DIS (1 << 17) /* disarm if 0 */
49 #define SYI_ARM (1 << 16) /* arm if 1 */
50 #define SYI_M_INT 0177777 /* interrupt */
52 /* Pseudo-device number for EOM/SKS mode 3 */
54 #define I_GETDEV3(x) ((((x) & 020046000) != 020046000)? ((x) & DEV_MASK): DEV_MASK)
56 #define TST_XFR(d,c) (xfr_req && dev_map[d][c])
57 #define SET_XFR(d,c) xfr_req = xfr_req | dev_map[d][c]
58 #define CLR_XFR(d,c) xfr_req = xfr_req & ~dev_map[d][c]
59 #define INV_DEV(d,c) (dev_dsp[d][c] == NULL)
60 #define VLD_DEV(d,c) (dev_dsp[d][c] != NULL)
61 #define TST_EOR(c) (chan_flag[c] & CHF_EOR)
62 #define QAILCE(a) (((a) >= POT_ILCY) && ((a) < (POT_ILCY + NUM_CHAN)))
64 uint8 chan_uar
[NUM_CHAN
]; /* unit addr */
65 uint16 chan_wcr
[NUM_CHAN
]; /* word count */
66 uint16 chan_mar
[NUM_CHAN
]; /* mem addr */
67 uint8 chan_dcr
[NUM_CHAN
]; /* data chain */
68 uint32 chan_war
[NUM_CHAN
]; /* word assembly */
69 uint8 chan_cpw
[NUM_CHAN
]; /* char per word */
70 uint8 chan_cnt
[NUM_CHAN
]; /* char count */
71 uint16 chan_mode
[NUM_CHAN
]; /* mode */
72 uint16 chan_flag
[NUM_CHAN
]; /* flags */
73 static const char *chname
[NUM_CHAN
] = {
74 "W", "Y", "C", "D", "E", "F", "G", "H"
77 extern uint32 M
[MAXMEMSIZE
]; /* memory */
78 extern uint32 int_req
; /* int req */
79 extern uint32 xfr_req
; /* xfer req */
80 extern uint32 alert
; /* pin/pot alert */
81 extern uint32 X
, EM2
, EM3
, OV
, ion
, bpt
;
82 extern uint32 nml_mode
, usr_mode
, rtc_pie
;
83 extern int32 stop_invins
, stop_invdev
, stop_inviop
;
84 extern int32 mon_usr_trap
;
87 extern DEVICE
*sim_devices
[];
89 t_stat
chan_reset (DEVICE
*dptr
);
90 t_stat
chan_read (int32 ch
);
91 t_stat
chan_write (int32 ch
);
92 void chan_write_mem (int32 ch
);
93 void chan_flush_war (int32 ch
);
94 uint32
chan_mar_inc (int32 ch
);
95 t_stat
chan_eor (int32 ch
);
96 t_stat
pot_ilc (uint32 num
, uint32
*dat
);
97 t_stat
pot_dcr (uint32 num
, uint32
*dat
);
98 t_stat
pin_adr (uint32 num
, uint32
*dat
);
99 t_stat
pot_fork (uint32 num
, uint32
*dat
);
100 t_stat
dev_disc (uint32 ch
, uint32 dev
);
101 t_stat
dev_wreor (uint32 ch
, uint32 dev
);
102 extern t_stat
pot_RL1 (uint32 num
, uint32
*dat
);
103 extern t_stat
pot_RL2 (uint32 num
, uint32
*dat
);
104 extern t_stat
pot_RL4 (uint32 num
, uint32
*dat
);
105 extern t_stat
pin_rads (uint32 num
, uint32
*dat
);
106 extern t_stat
pot_rada (uint32 num
, uint32
*dat
);
107 extern t_stat
pin_dsk (uint32 num
, uint32
*dat
);
108 extern t_stat
pot_dsk (uint32 num
, uint32
*dat
);
109 t_stat
pin_mux (uint32 num
, uint32
*dat
);
110 t_stat
pot_mux (uint32 num
, uint32
*dat
);
111 extern void set_dyn_map (void);
115 A device is modeled by its interactions with a channel. Devices can only be
116 accessed via channels. Each channel has its own device address space. This
117 means devices can only be accessed from a specific channel.
119 I/O operations start with a channel connect. The EOM instruction is passed
120 to the device via the conn routine. This routine is also used for non-channel
121 EOM's to the device. For channel connects, the device must remember the
124 The device responds (after a delay) by setting its XFR_RDY flag. This causes
125 the channel to invoke either the read or write routine (for input or output)
126 to get or put the next character. If the device is an asynchronous output
127 device, it calls routine chan_set_ordy to see if there is output available.
128 If there is, XFR_RDY is set; if not, the channel is marked to wake the
129 attached device when output is available. This prevents invalid rate errors.
131 Output may be terminated by a write end of record, a disconnect, or both.
132 Write end of record occurs when the word count reaches zero on an IORD or IORP
133 operation. It also occurs if a TOP instruction is issued. The device is
134 expected to respond by setting the end of record indicator in the channel,
135 which will in turn trigger an end of record interrupt.
137 When the channel operation completes, the channel disconnects and calls the
138 disconnect processor to perform any device specific cleanup. The differences
139 between write end of record and disconnect are subtle. On magtape output,
140 for example, both signal end of record; but write end of record allows the
141 magtape to continue moving, while disconnect halts its motion.
143 Valid devices supply a routine to handle potentially all I/O operations
144 (connect, disconnect, read, write, write end of record, sks). There are
145 separate routines for PIN and POT.
147 Channels could, optionally, handle 12b or 24b characters. The simulator can
151 t_stat
chan_show_reg (FILE *st
, UNIT
*uptr
, int32 val
, void *desc
);
154 t_stat (*pin
) (uint32 num
, uint32
*dat
); /* altnum, *dat */
155 t_stat (*pot
) (uint32 num
, uint32
*dat
); /* altnum, *dat */
158 /* Channel data structures
160 chan_dev channel device descriptor
161 chan_unit channel unit descriptor
162 chan_reg channel register list
165 UNIT chan_unit
= { UDATA (NULL
, 0, 0) };
168 { BRDATA (UAR
, chan_uar
, 8, 6, NUM_CHAN
) },
169 { BRDATA (WCR
, chan_wcr
, 8, 15, NUM_CHAN
) },
170 { BRDATA (MAR
, chan_mar
, 8, 16, NUM_CHAN
) },
171 { BRDATA (DCR
, chan_dcr
, 8, 6, NUM_CHAN
) },
172 { BRDATA (WAR
, chan_war
, 8, 24, NUM_CHAN
) },
173 { BRDATA (CPW
, chan_cpw
, 8, 2, NUM_CHAN
) },
174 { BRDATA (CNT
, chan_cnt
, 8, 3, NUM_CHAN
) },
175 { BRDATA (MODE
, chan_mode
, 8, 12, NUM_CHAN
) },
176 { BRDATA (FLAG
, chan_flag
, 8, CHF_N_FLG
, NUM_CHAN
) },
181 { MTAB_XTD
| MTAB_VDV
| MTAB_NMO
, CHAN_W
, "W", NULL
,
182 NULL
, &chan_show_reg
, NULL
},
183 { MTAB_XTD
| MTAB_VDV
| MTAB_NMO
, CHAN_Y
, "Y", NULL
,
184 NULL
, &chan_show_reg
, NULL
},
185 { MTAB_XTD
| MTAB_VDV
| MTAB_NMO
, CHAN_C
, "C", NULL
,
186 NULL
, &chan_show_reg
, NULL
},
187 { MTAB_XTD
| MTAB_VDV
| MTAB_NMO
, CHAN_D
, "D", NULL
,
188 NULL
, &chan_show_reg
, NULL
},
189 { MTAB_XTD
| MTAB_VDV
| MTAB_NMO
, CHAN_E
, "E", NULL
,
190 NULL
, &chan_show_reg
, NULL
},
191 { MTAB_XTD
| MTAB_VDV
| MTAB_NMO
, CHAN_F
, "F", NULL
,
192 NULL
, &chan_show_reg
, NULL
},
193 { MTAB_XTD
| MTAB_VDV
| MTAB_NMO
, CHAN_G
, "G", NULL
,
194 NULL
, &chan_show_reg
, NULL
},
195 { MTAB_XTD
| MTAB_VDV
| MTAB_NMO
, CHAN_H
, "H", NULL
,
196 NULL
, &chan_show_reg
, NULL
}
200 "CHAN", &chan_unit
, chan_reg
, chan_mod
,
202 NULL
, NULL
, &chan_reset
,
208 static const uint32 int_zc
[8] = {
209 INT_WZWC
, INT_YZWC
, INT_CZWC
, INT_DZWC
,
210 INT_EZWC
, INT_FZWC
, INT_GZWC
, INT_HZWC
213 static const uint32 int_er
[8] = {
214 INT_WEOR
, INT_YEOR
, INT_CEOR
, INT_DEOR
,
215 INT_EEOR
, INT_FEOR
, INT_GEOR
, INT_HEOR
218 /* dev_map maps device and channel numbers to a transfer flag masks */
220 uint32 dev_map
[64][NUM_CHAN
];
222 /* dev_dsp maps device and channel numbers to dispatch routines */
224 t_stat (*dev_dsp
[64][NUM_CHAN
])() = { NULL
};
226 /* dev3_dsp maps system device numbers to dispatch routines */
228 t_stat (*dev3_dsp
[64])() = { NULL
};
230 /* dev_alt maps alert numbers to dispatch routines */
232 struct aldisp dev_alt
[] = {
234 { NULL
, &pot_ilc
}, { NULL
, &pot_ilc
},
235 { NULL
, &pot_ilc
}, { NULL
, &pot_ilc
},
236 { NULL
, &pot_ilc
}, { NULL
, &pot_ilc
},
237 { NULL
, &pot_ilc
}, { NULL
, &pot_ilc
},
238 { NULL
, &pot_dcr
}, { NULL
, &pot_dcr
},
239 { NULL
, &pot_dcr
}, { NULL
, &pot_dcr
},
240 { NULL
, &pot_dcr
}, { NULL
, &pot_dcr
},
241 { NULL
, &pot_dcr
}, { NULL
, &pot_dcr
},
242 { &pin_adr
, NULL
}, { &pin_adr
, NULL
},
243 { &pin_adr
, NULL
}, { &pin_adr
, NULL
},
244 { &pin_adr
, NULL
}, { &pin_adr
, NULL
},
245 { &pin_adr
, NULL
}, { &pin_adr
, NULL
},
246 { NULL
, &pot_RL1
}, { NULL
, &pot_RL2
},
248 { &pin_rads
, NULL
}, { NULL
, &pot_rada
},
249 { &pin_dsk
, &pot_dsk
}, { NULL
, &pot_fork
},
250 { &pin_mux
, &pot_mux
}
253 /* Single word I/O instructions */
255 t_stat
op_wyim (uint32 inst
, uint32
*dat
)
259 ch
= (inst
& 000200000)? CHAN_W
: CHAN_Y
; /* get chan# */
260 dev
= chan_uar
[ch
] & DEV_MASK
; /* get dev # */
261 if (chan_cnt
[ch
] <= chan_cpw
[ch
]) { /* buffer empty? */
262 if (dev
== 0) return STOP_INVIOP
; /* no device? dead */
263 return STOP_IONRDY
; /* hang until full */
265 *dat
= chan_war
[ch
]; /* get data */
266 chan_war
[ch
] = 0; /* reset war */
267 chan_cnt
[ch
] = 0; /* reset cnt */
271 t_stat
op_miwy (uint32 inst
, uint32 dat
)
275 ch
= (inst
& 000200000)? CHAN_W
: CHAN_Y
; /* get chan# */
276 dev
= chan_uar
[ch
] & DEV_MASK
; /* get dev # */
277 if (chan_cnt
[ch
] != 0) { /* buffer full? */
278 if (dev
== 0) return STOP_INVIOP
; /* no device? dead */
279 return STOP_IONRDY
; /* hang until full */
281 chan_war
[ch
] = dat
; /* get data */
282 chan_cnt
[ch
] = chan_cpw
[ch
] + 1; /* buffer full */
283 if (chan_flag
[ch
] & CHF_OWAK
) { /* output wake? */
284 if (VLD_DEV (dev
, ch
)) SET_XFR (dev
, ch
); /* wake channel */
285 chan_flag
[ch
] = chan_flag
[ch
] & ~CHF_OWAK
; /* clear wake */
290 t_stat
op_pin (uint32
*dat
)
292 uint32 al
= alert
; /* local copy */
294 alert
= 0; /* clear alert */
295 if ((al
== 0) || (dev_alt
[al
].pin
== NULL
)) CRETIOP
; /* inv alert? */
296 return dev_alt
[al
].pin (al
, dat
); /* PIN from dev */
299 t_stat
op_pot (uint32 dat
)
301 uint32 al
= alert
; /* local copy */
303 alert
= 0; /* clear alert */
304 if ((al
== 0) || (dev_alt
[al
].pot
== NULL
)) CRETIOP
; /* inv alert? */
305 return dev_alt
[al
].pot (al
, &dat
); /* POT to dev */
310 t_stat
op_eomd (uint32 inst
)
312 uint32 mod
= I_GETIOMD (inst
); /* get mode */
313 uint32 ch
= I_GETEOCH (inst
); /* get chan # */
314 uint32 dev
= inst
& DEV_MASK
; /* get dev # */
315 uint32 ch_dev
= chan_uar
[ch
] & DEV_MASK
; /* chan curr dev # */
320 case 0: /* IO control */
321 if (dev
) { /* new dev? */
322 if (ch_dev
) CRETIOP
; /* chan act? err */
323 if (INV_DEV (dev
, ch
)) CRETDEV
; /* inv dev? err */
324 chan_war
[ch
] = chan_cnt
[ch
] = 0; /* init chan */
325 chan_flag
[ch
] = chan_dcr
[ch
] = 0;
326 chan_mode
[ch
] = chan_uar
[ch
] = 0;
327 if (ch
>= CHAN_E
) chan_mode
[ch
] = CHM_CE
;
328 if (r
= dev_dsp
[dev
][ch
] (IO_CONN
, inst
, NULL
)) /* connect */
330 if ((inst
& I_IND
) || (ch
>= CHAN_C
)) { /* C-H? alert ilc */
331 alert
= POT_ILCY
+ ch
;
332 chan_mar
[ch
] = chan_wcr
[ch
] = 0;
334 if (chan_flag
[ch
] & CHF_24B
) chan_cpw
[ch
] = 0; /* 24B? 1 ch/wd */
335 else if (chan_flag
[ch
] & CHF_12B
) /* 12B? 2 ch/wd */
336 chan_cpw
[ch
] = CHC_GETCPW (inst
) & 1;
337 else chan_cpw
[ch
] = CHC_GETCPW (inst
); /* 6b, 1-4 ch/wd */
338 chan_uar
[ch
] = dev
; /* connected */
339 if ((dev
& DEV_OUT
) && ion
&& !QAILCE (alert
)) /* out, prog IO? */
340 int_req
= int_req
| int_zc
[ch
]; /* initial intr */
342 else return dev_disc (ch
, ch_dev
); /* disconnect */
345 case 1: /* buf control */
346 if (QAILCE (alert
)) { /* ilce alerted? */
347 ch
= alert
- POT_ILCY
; /* derive chan */
348 if (ch
>= CHAN_E
) inst
= inst
| CHM_CE
; /* DACC? ext */
349 chan_mode
[ch
] = inst
; /* save mode */
350 chan_mar
[ch
] = (CHM_GETHMA (inst
) << 14) | /* get hi mar */
351 (chan_mar
[ch
] & CHI_M_MA
);
352 chan_wcr
[ch
] = (CHM_GETHWC (inst
) << 10) | /* get hi wc */
353 (chan_wcr
[ch
] & CHI_M_WC
);
355 else if (dev
) { /* dev EOM */
356 if (INV_DEV (dev
, ch
)) CRETDEV
; /* inv dev? err */
357 return dev_dsp
[dev
][ch
] (IO_EOM1
, inst
, NULL
);
359 else { /* chan EOM */
360 inst
= inst
& 047677;
361 if (inst
== 040000) { /* alert ilce */
362 alert
= POT_ILCY
+ ch
;
363 chan_mar
[ch
] = chan_wcr
[ch
] = 0;
365 else if (inst
== 002000) alert
= POT_ADRY
+ ch
; /* alert addr */
366 else if (inst
== 001000) alert
= POT_DCRY
+ ch
; /* alert DCR */
367 else if (inst
== 004000) { /* term output */
368 if (ch_dev
& DEV_OUT
) { /* to output dev? */
369 if (chan_cnt
[ch
] || (chan_flag
[ch
] & CHF_ILCE
)) /* busy, DMA? */
370 chan_flag
[ch
] = chan_flag
[ch
] | CHF_TOP
; /* TOP pending */
371 else return dev_wreor (ch
, ch_dev
); /* idle, write EOR */
373 else if (ch_dev
& DEV_MT
) { /* change to scan? */
374 chan_uar
[ch
] = chan_uar
[ch
] | DEV_MTS
; /* change dev addr */
375 chan_flag
[ch
] = chan_flag
[ch
] | CHF_SCAN
; /* set scan flag */
376 } /* end else change scan */
377 } /* end else term output */
378 } /* end else chan EOM */
381 case 2: /* internal */
382 if (ch
>= CHAN_E
) { /* EOD? */
383 if (inst
& 00300) { /* set EM? */
384 if (inst
& 00100) EM2
= inst
& 07;
385 if (inst
& 00200) EM3
= (inst
>> 3) & 07;
390 if (inst
& 00001) OV
= 0; /* clr OV */
391 if (inst
& 00002) ion
= 1; /* ion */
392 else if (inst
& 00004) ion
= 0; /* iof */
393 if ((inst
& 00010) && (((X
>> 1) ^ X
) & EXPS
)) OV
= 1;
394 if (inst
& 00020) alert
= POT_SYSI
; /* alert sys int */
395 if (inst
& 00100) rtc_pie
= 1; /* arm clk pls */
396 else if (inst
& 00200) rtc_pie
= 0; /* disarm pls */
397 if ((inst
& 01400) == 01400) alert
= POT_RL4
; /* alert RL4 */
398 else if (inst
& 00400) alert
= POT_RL1
; /* alert RL1 */
399 else if (inst
& 01000) alert
= POT_RL2
; /* alert RL2 */
400 if (inst
& 02000) { /* nml to mon */
401 nml_mode
= usr_mode
= 0;
402 if (inst
& 00400) mon_usr_trap
= 1;
406 case 3: /* special */
407 dev
= I_GETDEV3 (inst
); /* special device */
408 if (dev3_dsp
[dev
]) /* defined? */
409 return dev3_dsp
[dev
] (IO_CONN
, inst
, NULL
);
416 /* Skip if not signal */
418 t_stat
op_sks (uint32 inst
, uint32
*dat
)
420 uint32 mod
= I_GETIOMD (inst
); /* get mode */
421 uint32 ch
= I_GETSKCH (inst
); /* get chan # */
422 uint32 dev
= inst
& DEV_MASK
; /* get dev # */
425 if ((ch
== 4) && !(inst
& 037774)) { /* EM test */
426 if (((inst
& 0001) && (EM2
!= 2)) ||
427 ((inst
& 0002) && (EM3
!= 3))) *dat
= 1;
432 case 1: /* ch, dev */
433 if (dev
) { /* device */
434 if (INV_DEV (dev
, ch
)) CRETDEV
; /* inv dev? err */
435 dev_dsp
[dev
][ch
] (IO_SKS
, inst
, dat
); /* do test */
438 if (((inst
& 04000) && (chan_uar
[ch
] == 0)) ||
439 ((inst
& 02000) && (chan_wcr
[ch
] == 0)) ||
440 ((inst
& 01000) && ((chan_flag
[ch
] & CHF_ERR
) == 0)) ||
441 ((inst
& 00400) && (chan_flag
[ch
] & CHF_IREC
))) *dat
= 1;
445 case 2: /* internal test */
446 if (inst
& 0001) { /* test OV */
447 *dat
= OV
^ 1; /* skip if off */
448 OV
= 0; /* and reset */
451 if (((inst
& 00002) && !ion
) || /* ion, bpt test */
452 ((inst
& 00004) && ion
) ||
453 ((inst
& 00010) && ((chan_flag
[CHAN_W
] & CHF_ERR
) == 0)) ||
454 ((inst
& 00020) && ((chan_flag
[CHAN_Y
] & CHF_ERR
) == 0)) ||
455 ((inst
& 00040) && ((bpt
& 001) == 0)) ||
456 ((inst
& 00100) && ((bpt
& 002) == 0)) ||
457 ((inst
& 00200) && ((bpt
& 004) == 0)) ||
458 ((inst
& 00400) && ((bpt
& 010) == 0)) ||
459 ((inst
& 01000) && (chan_uar
[CHAN_W
] == 0)) ||
460 ((inst
& 02000) && (chan_uar
[CHAN_Y
] == 0))) *dat
= 1;
463 case 3: /* special */
464 dev
= I_GETDEV3 (inst
); /* special device */
465 if (dev3_dsp
[dev
]) dev3_dsp
[dev
] (IO_SKS
, inst
, dat
);
472 /* PIN/POT routines */
474 t_stat
pot_ilc (uint32 num
, uint32
*dat
)
476 uint32 ch
= num
- POT_ILCY
;
478 chan_mar
[ch
] = (chan_mar
[ch
] & ~CHI_M_MA
) | CHI_GETMA (*dat
);
479 chan_wcr
[ch
] = (chan_wcr
[ch
] & ~CHI_M_WC
) | CHI_GETWC (*dat
);
480 chan_flag
[ch
] = chan_flag
[ch
] | CHF_ILCE
;
484 t_stat
pot_dcr (uint32 num
, uint32
*dat
)
486 uint32 ch
= num
- POT_DCRY
;
488 chan_dcr
[ch
] = (*dat
) & (CHD_INT
| CHD_PAGE
);
489 chan_flag
[ch
] = chan_flag
[ch
] | CHF_DCHN
;
493 t_stat
pin_adr (uint32 num
, uint32
*dat
)
495 uint32 ch
= num
- POT_ADRY
;
497 *dat
= chan_mar
[ch
] & PAMASK
;
501 /* System interrupt POT.
503 The SDS 940 timesharing system uses a permanently asserted
504 system interrupt as a way of forking the teletype input
505 interrupt handler to a lower priority. The interrupt is
506 armed to set up the fork, and disarmed in the fork routine */
508 t_stat
pot_fork (uint32 num
, uint32
*dat
)
510 uint32 igrp
= SYI_GETGRP (*dat
); /* get group */
511 uint32 fbit
= (1 << (VEC_FORK
& 017)); /* bit in group */
513 if (igrp
== (VEC_FORK
/ 020)) { /* right group? */
514 if ((*dat
& SYI_ARM
) && (*dat
& fbit
)) /* arm, bit set? */
515 int_req
= int_req
| INT_FORK
;
516 if ((*dat
& SYI_DIS
) && !(*dat
& fbit
)) /* disarm, bit clr? */
517 int_req
= int_req
& ~INT_FORK
;
522 /* Channel read invokes the I/O device to get the next character and,
523 if not end of record, assembles it into the word assembly register.
524 If the interlace is on, the full word is stored in memory.
525 The key difference points for the various terminal functions are
527 end of record comp: EOT interrupt
528 IORD, IOSD: EOR interrupt, disconnect
529 IORP, IOSP: EOR interrupt, interrecord
530 interlace off: comp: EOW interrupt
532 IOSD, IOSP: overrun error
533 --wcr == 0: comp: clear interlace
534 IORD, IORP, IOSP: ZWC interrupt
535 IOSD: ZWC interrupt, EOR interrupt, disconnect
537 Note that the channel can be disconnected if CHN_EOR is set, but must
538 not be if XFR_REQ is set */
540 t_stat
chan_read (int32 ch
)
543 uint32 dev
= chan_uar
[ch
] & DEV_MASK
;
544 uint32 tfnc
= CHM_GETFNC (chan_mode
[ch
]);
547 if (dev
&& TST_XFR (dev
, ch
)) { /* ready to xfr? */
548 if (INV_DEV (dev
, ch
)) CRETIOP
; /* can't read? */
549 r
= dev_dsp
[dev
][ch
] (IO_READ
, dev
, &dat
); /* read data */
550 if (r
) chan_flag
[ch
] = chan_flag
[ch
] | CHF_ERR
; /* error? */
551 if (chan_flag
[ch
] & CHF_24B
) chan_war
[ch
] = dat
; /* 24B? */
552 else if (chan_flag
[ch
] & CHF_12B
) /* 12B? */
553 chan_war
[ch
] = ((chan_war
[ch
] << 12) | (dat
& 07777)) & DMASK
;
554 else chan_war
[ch
] = ((chan_war
[ch
] << 6) | (dat
& 077)) & DMASK
;
555 if (chan_flag
[ch
] & CHF_SCAN
) /* scanning? */
556 chan_cnt
[ch
] = chan_cpw
[ch
]; /* never full */
557 else chan_cnt
[ch
] = chan_cnt
[ch
] + 1; /* insert char */
558 if (chan_cnt
[ch
] > chan_cpw
[ch
]) { /* full? */
559 if (chan_flag
[ch
] & CHF_ILCE
) { /* interlace on? */
560 chan_write_mem (ch
); /* write to mem */
561 if (chan_wcr
[ch
] == 0) { /* wc zero? */
562 chan_flag
[ch
] = chan_flag
[ch
] & ~CHF_ILCE
; /* clr interlace */
563 if ((tfnc
!= CHM_COMP
) && (chan_mode
[ch
] & CHM_ZC
))
564 int_req
= int_req
| int_zc
[ch
]; /* zwc interrupt */
565 if (tfnc
== CHM_IOSD
) { /* IOSD? also EOR */
566 if (chan_mode
[ch
] & CHM_ER
) int_req
= int_req
| int_er
[ch
];
567 dev_disc (ch
, dev
); /* disconnect */
569 } /* end if wcr == 0 */
570 } /* end if ilce on */
571 else { /* interlace off */
572 if (TST_EOR (ch
)) return chan_eor (ch
); /* eor? */
573 if (tfnc
== CHM_COMP
) { /* C: EOW, intr */
574 if (ion
) int_req
= int_req
| int_zc
[ch
];
576 else if (tfnc
& CHM_SGNL
) /* Sx: error */
577 chan_flag
[ch
] = chan_flag
[ch
] | CHF_ERR
;
578 else chan_cnt
[ch
] = chan_cpw
[ch
]; /* Rx: ignore */
579 } /* end else ilce */
582 if (TST_EOR (ch
)) { /* end record? */
583 if (tfnc
== CHM_COMP
) chan_flush_war (ch
); /* C: fill war */
584 else if (chan_cnt
[ch
]) { /* RX, CX: fill? */
585 chan_flush_war (ch
); /* fill war */
586 if (chan_flag
[ch
] & CHF_ILCE
) /* ilce on? store */
588 } /* end else if cnt */
589 return chan_eor (ch
); /* eot/eor int */
594 void chan_write_mem (int32 ch
)
596 WriteP (chan_mar
[ch
], chan_war
[ch
]); /* write to mem */
597 chan_mar
[ch
] = chan_mar_inc (ch
); /* incr mar */
598 chan_wcr
[ch
] = (chan_wcr
[ch
] - 1) & 077777; /* decr wcr */
599 chan_war
[ch
] = 0; /* reset war */
600 chan_cnt
[ch
] = 0; /* reset cnt */
604 void chan_flush_war (int32 ch
)
606 int32 i
= (chan_cpw
[ch
] - chan_cnt
[ch
]) + 1;
609 if (chan_flag
[ch
] & CHF_24B
) chan_war
[ch
] = 0;
610 else if (chan_flag
[ch
] & CHF_12B
)
611 chan_war
[ch
] = (chan_war
[ch
] << 12) & DMASK
;
612 else chan_war
[ch
] = (chan_war
[ch
] << (i
* 6)) & DMASK
;
613 chan_cnt
[ch
] = chan_cpw
[ch
] + 1;
618 /* Channel write gets the next character and sends it to the I/O device.
619 If this is the last character in an interlace operation, the end of
620 record operation is invoked.
621 The key difference points for the various terminal functions are
623 end of record: comp: EOT interrupt
624 IORD, IOSD: EOR interrupt, disconnect
625 IORP, IOSP: EOR interrupt, interrecord
626 interlace off: if not end of record, EOW interrupt
627 --wcr == 0: comp: EOT interrupt, disconnect
629 IOSD: ZWC interrupt, disconnect
630 IOSP: ZWC interrupt, interrecord
632 t_stat
chan_write (int32 ch
)
635 uint32 dev
= chan_uar
[ch
] & DEV_MASK
;
636 uint32 tfnc
= CHM_GETFNC (chan_mode
[ch
]);
639 if (dev
&& TST_XFR (dev
, ch
)) { /* ready to xfr? */
640 if (INV_DEV (dev
, ch
)) CRETIOP
; /* invalid dev? */
641 if (chan_cnt
[ch
] == 0) { /* buffer empty? */
642 if (chan_flag
[ch
] & CHF_ILCE
) { /* interlace on? */
643 chan_war
[ch
] = ReadP (chan_mar
[ch
]);
644 chan_mar
[ch
] = chan_mar_inc (ch
); /* incr mar */
645 chan_wcr
[ch
] = (chan_wcr
[ch
] - 1) & 077777; /* decr mar */
646 chan_cnt
[ch
] = chan_cpw
[ch
] + 1; /* set cnt */
648 else { /* ilce off */
649 CLR_XFR (dev
, ch
); /* cant xfr */
650 if (TST_EOR (dev
)) return chan_eor (ch
); /* EOR? */
651 chan_flag
[ch
] = chan_flag
[ch
] | CHF_ERR
; /* rate err */
653 } /* end else ilce */
655 chan_cnt
[ch
] = chan_cnt
[ch
] - 1; /* decr cnt */
656 if (chan_flag
[ch
] & CHF_24B
) dat
= chan_war
[ch
]; /* 24B? */
657 else if (chan_flag
[ch
] & CHF_12B
) { /* 12B? */
658 dat
= (chan_war
[ch
] >> 12) & 07777; /* get halfword */
659 chan_war
[ch
] = (chan_war
[ch
] << 12) & DMASK
; /* remove from war */
662 dat
= (chan_war
[ch
] >> 18) & 077; /* get char */
663 chan_war
[ch
] = (chan_war
[ch
] << 6) & DMASK
; /* remove from war */
665 r
= dev_dsp
[dev
][ch
] (IO_WRITE
, dev
, &dat
); /* write */
666 if (r
) chan_flag
[ch
] = chan_flag
[ch
] | CHF_ERR
; /* error? */
667 if (chan_cnt
[ch
] == 0) { /* buf empty? */
668 if (chan_flag
[ch
] & CHF_ILCE
) { /* ilce on? */
669 if (chan_wcr
[ch
] == 0) { /* wc now 0? */
670 chan_flag
[ch
] = chan_flag
[ch
] & ~CHF_ILCE
; /* ilc off */
671 if (tfnc
== CHM_COMP
) { /* compatible? */
672 if (ion
) int_req
= int_req
| int_zc
[ch
];
673 dev_disc (ch
, dev
); /* disconnnect */
675 else { /* extended */
676 if (chan_mode
[ch
] & CHM_ZC
) /* ZWC int */
677 int_req
= int_req
| int_zc
[ch
];
678 if (tfnc
== CHM_IOSD
) { /* SD */
679 if (chan_mode
[ch
] & CHM_ER
) /* EOR int */
680 int_req
= int_req
| int_er
[ch
];
681 dev_disc (ch
, dev
); /* disconnnect */
683 else if (!(tfnc
&& CHM_SGNL
) || /* IORx or IOSP TOP? */
684 (chan_flag
[ch
] & CHF_TOP
))
685 dev_wreor (ch
, dev
); /* R: write EOR */
686 chan_flag
[ch
] = chan_flag
[ch
] & ~CHF_TOP
;
687 } /* end else comp */
690 else if (chan_flag
[ch
] & CHF_TOP
) { /* off, TOP pending? */
691 chan_flag
[ch
] = chan_flag
[ch
] & ~CHF_TOP
; /* clear TOP */
692 dev_wreor (ch
, dev
); /* write EOR */
694 else if (ion
) int_req
= int_req
| int_zc
[ch
]; /* no TOP, EOW intr */
697 if (TST_EOR (ch
)) return chan_eor (ch
); /* eor rcvd? */
703 uint32
chan_mar_inc (int32 ch
)
705 uint32 t
= (chan_mar
[ch
] + 1) & PAMASK
; /* incr mar */
707 if ((chan_flag
[ch
] & CHF_DCHN
) && ((t
& VA_POFF
) == 0)) { /* chain? */
708 chan_flag
[ch
] = chan_flag
[ch
] & ~CHF_DCHN
; /* clr flag */
709 if (chan_dcr
[ch
] & CHD_INT
) /* if armed, intr */
710 int_req
= int_req
| int_zc
[ch
];
711 t
= (chan_dcr
[ch
] & CHD_PAGE
) << VA_V_PN
; /* new mar */
716 /* End of record action */
718 t_stat
chan_eor (int32 ch
)
720 uint32 tfnc
= CHM_GETFNC (chan_mode
[ch
]);
721 uint32 dev
= chan_uar
[ch
] & DEV_MASK
;
723 chan_flag
[ch
] = chan_flag
[ch
] & ~(CHF_EOR
| CHF_ILCE
); /* clr eor, ilce */
724 if (((tfnc
== CHM_COMP
) && ion
) || (chan_mode
[ch
] & CHM_ER
))
725 int_req
= int_req
| int_er
[ch
]; /* EOT/EOR? */
726 if (dev
&& (tfnc
& CHM_PROC
)) /* P, still conn? */
727 chan_flag
[ch
] = chan_flag
[ch
] | CHF_IREC
; /* interrecord */
728 else return dev_disc (ch
, dev
); /* disconnect */
732 /* Utility routines */
734 t_stat
dev_disc (uint32 ch
, uint32 dev
)
736 chan_uar
[ch
] = 0; /* disconnect */
737 if (dev_dsp
[dev
][ch
]) return dev_dsp
[dev
][ch
] (IO_DISC
, dev
, NULL
);
741 t_stat
dev_wreor (uint32 ch
, uint32 dev
)
743 if (dev_dsp
[dev
][ch
]) return dev_dsp
[dev
][ch
] (IO_WREOR
, dev
, NULL
);
744 chan_flag
[ch
] = chan_flag
[ch
] | CHF_EOR
; /* set eor */
748 /* Externally visible routines */
751 t_stat
chan_process (void)
756 for (i
= 0; i
< NUM_CHAN
; i
++) { /* loop thru */
757 dev
= chan_uar
[i
] & DEV_MASK
; /* get dev */
758 if ((dev
&& TST_XFR (dev
, i
)) || TST_EOR (i
)) { /* chan active? */
759 if (dev
& DEV_OUT
) r
= chan_write (i
); /* write */
760 else r
= chan_read (i
); /* read */
767 /* Test for channel active */
769 t_bool
chan_testact (void)
773 for (i
= 0; i
< NUM_CHAN
; i
++) {
774 dev
= chan_uar
[i
] & DEV_MASK
;
775 if ((dev
&& TST_XFR (dev
, i
)) || TST_EOR (i
)) return 1;
780 /* Async output device ready for more data */
782 void chan_set_ordy (int32 ch
)
784 if ((ch
>= 0) && (ch
< NUM_CHAN
)) {
785 int32 dev
= chan_uar
[ch
] & DEV_MASK
; /* get dev */
786 if (chan_cnt
[ch
] || (chan_flag
[ch
] & CHF_ILCE
)) /* buf or ilce? */
787 SET_XFR (dev
, ch
); /* set xfr flg */
788 else chan_flag
[ch
] = chan_flag
[ch
] | CHF_OWAK
; /* need wakeup */
793 /* Set flag in channel */
795 void chan_set_flag (int32 ch
, uint32 fl
)
797 if ((ch
>= 0) && (ch
< NUM_CHAN
)) chan_flag
[ch
] = chan_flag
[ch
] | fl
;
801 /* Set UAR in channel */
803 void chan_set_uar (int32 ch
, uint32 dev
)
805 if ((ch
>= 0) && (ch
< NUM_CHAN
)) chan_uar
[ch
] = dev
& DEV_MASK
;
809 /* Disconnect channel */
811 void chan_disc (int32 ch
)
813 if ((ch
>= 0) && (ch
< NUM_CHAN
)) chan_uar
[ch
] = 0;
819 t_stat
chan_reset (DEVICE
*dptr
)
824 for (i
= 0; i
< NUM_CHAN
; i
++) {
838 /* Channel assignment routines */
840 t_stat
set_chan (UNIT
*uptr
, int32 val
, char *sptr
, void *desc
)
846 if (sptr
== NULL
) return SCPE_ARG
; /* valid args? */
847 if (uptr
== NULL
) return SCPE_IERR
;
848 dptr
= find_dev_from_unit (uptr
);
849 if (dptr
== NULL
) return SCPE_IERR
;
850 dibp
= (DIB
*) dptr
->ctxt
;
851 if (dibp
== NULL
) return SCPE_IERR
;
852 for (i
= 0; i
< NUM_CHAN
; i
++) { /* match input */
853 if (strcmp (sptr
, chname
[i
]) == 0) { /* find string */
854 if (val
&& !(val
& (1 << i
))) return SCPE_ARG
; /* legal? */
855 dibp
->chan
= i
; /* store new */
862 t_stat
show_chan (FILE *st
, UNIT
*uptr
, int32 val
, void *desc
)
867 if (uptr
== NULL
) return SCPE_IERR
;
868 dptr
= find_dev_from_unit (uptr
);
869 if (dptr
== NULL
) return SCPE_IERR
;
870 dibp
= (DIB
*) dptr
->ctxt
;
871 if (dibp
== NULL
) return SCPE_IERR
;
872 fprintf (st
, "channel=%s", chname
[dibp
->chan
]);
876 /* Init device tables */
878 t_bool
io_init (void)
884 uint32 i
, j
, dev
, doff
;
886 /* Clear dispatch table, device map */
888 for (i
= 0; i
< NUM_CHAN
; i
++) {
889 for (j
= 0; j
< (DEV_MASK
+ 1); j
++) {
890 dev_dsp
[j
][i
] = NULL
;
895 /* Test each device for conflict; add to map; init tables */
897 for (i
= 0; dptr
= sim_devices
[i
]; i
++) { /* loop thru devices */
898 dibp
= (DIB
*) dptr
->ctxt
; /* get DIB */
899 if ((dibp
== NULL
) || (dptr
->flags
& DEV_DIS
)) continue; /* exist, enabled? */
900 ch
= dibp
->chan
; /* get channel */
901 dev
= dibp
->dev
; /* get device num */
902 if (ch
< 0) dev3_dsp
[dev
] = dibp
->iop
; /* special device */
904 if (dibp
->tplt
== NULL
) return TRUE
; /* must have template */
905 for (tplp
= dibp
->tplt
; tplp
->num
; tplp
++) { /* loop thru templates */
906 for (j
= 0; j
< tplp
->num
; j
++) { /* repeat as needed */
907 doff
= dev
+ tplp
->off
+ j
; /* get offset dnum */
908 if (dev_map
[doff
][ch
]) { /* slot in use? */
909 printf ("Device number conflict, chan = %s, devno = %02o\n",
911 if (sim_log
) fprintf (sim_log
,
912 "Device number conflict, chan = %s, dev = %02o\n",
916 dev_map
[doff
][ch
] = dibp
->xfr
; /* set xfr flag */
917 dev_dsp
[doff
][ch
] = dibp
->iop
; /* set dispatch */
925 /* Display channel state */
927 t_stat
chan_show_reg (FILE *st
, UNIT
*uptr
, int32 val
, void *desc
)
929 if ((val
< 0) || (val
>= NUM_CHAN
)) return SCPE_IERR
;
930 fprintf (st
, "UAR: %02o\n", chan_uar
[val
]);
931 fprintf (st
, "WCR: %05o\n", chan_wcr
[val
]);
932 fprintf (st
, "MAR: %06o\n", chan_mar
[val
]);
933 fprintf (st
, "DCR: %02o\n", chan_dcr
[val
]);
934 fprintf (st
, "WAR: %08o\n", chan_war
[val
]);
935 fprintf (st
, "CPW: %o\n", chan_cpw
[val
]);
936 fprintf (st
, "CNT: %o\n", chan_cnt
[val
]);
937 fprintf (st
, "MODE: %03o\n", chan_mode
[val
]);
938 fprintf (st
, "FLAG: %04o\n", chan_flag
[val
]);