First Commit of my working state
[simh.git] / SDS / sds_io.c
1 /* sds_io.c: SDS 940 I/O simulator
2
3 Copyright (c) 2001-2005, 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 #include "sds_defs.h"
28
29 /* Data chain word */
30
31 #define CHD_INT 040 /* int on chain */
32 #define CHD_PAGE 037 /* new page # */
33
34 /* Interlace POT */
35
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)
42
43 /* System interrupt POT */
44
45 #define SYI_V_GRP 18 /* group */
46 #define SYI_M_GRP 077
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 */
51
52 /* Pseudo-device number for EOM/SKS mode 3 */
53
54 #define I_GETDEV3(x) ((((x) & 020046000) != 020046000)? ((x) & DEV_MASK): DEV_MASK)
55
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)))
63
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"
75 };
76
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;
85 extern UNIT cpu_unit;
86 extern FILE *sim_log;
87 extern DEVICE *sim_devices[];
88
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);
112
113 /* SDS I/O model
114
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.
118
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
122 channel number.
123
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.
130
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.
136
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.
142
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.
146
147 Channels could, optionally, handle 12b or 24b characters. The simulator can
148 support all widths.
149 */
150
151 t_stat chan_show_reg (FILE *st, UNIT *uptr, int32 val, void *desc);
152
153 struct aldisp {
154 t_stat (*pin) (uint32 num, uint32 *dat); /* altnum, *dat */
155 t_stat (*pot) (uint32 num, uint32 *dat); /* altnum, *dat */
156 };
157
158 /* Channel data structures
159
160 chan_dev channel device descriptor
161 chan_unit channel unit descriptor
162 chan_reg channel register list
163 */
164
165 UNIT chan_unit = { UDATA (NULL, 0, 0) };
166
167 REG chan_reg[] = {
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) },
177 { NULL }
178 };
179
180 MTAB chan_mod[] = {
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 }
197 };
198
199 DEVICE chan_dev = {
200 "CHAN", &chan_unit, chan_reg, chan_mod,
201 1, 8, 8, 1, 8, 8,
202 NULL, NULL, &chan_reset,
203 NULL, NULL, NULL
204 };
205
206 /* Tables */
207
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
211 };
212
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
216 };
217
218 /* dev_map maps device and channel numbers to a transfer flag masks */
219
220 uint32 dev_map[64][NUM_CHAN];
221
222 /* dev_dsp maps device and channel numbers to dispatch routines */
223
224 t_stat (*dev_dsp[64][NUM_CHAN])() = { NULL };
225
226 /* dev3_dsp maps system device numbers to dispatch routines */
227
228 t_stat (*dev3_dsp[64])() = { NULL };
229
230 /* dev_alt maps alert numbers to dispatch routines */
231
232 struct aldisp dev_alt[] = {
233 { NULL, NULL },
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 },
247 { NULL, &pot_RL4 },
248 { &pin_rads, NULL }, { NULL, &pot_rada },
249 { &pin_dsk, &pot_dsk }, { NULL, &pot_fork },
250 { &pin_mux, &pot_mux }
251 };
252
253 /* Single word I/O instructions */
254
255 t_stat op_wyim (uint32 inst, uint32 *dat)
256 {
257 int32 ch, dev;
258
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 */
264 }
265 *dat = chan_war[ch]; /* get data */
266 chan_war[ch] = 0; /* reset war */
267 chan_cnt[ch] = 0; /* reset cnt */
268 return SCPE_OK;
269 }
270
271 t_stat op_miwy (uint32 inst, uint32 dat)
272 {
273 int32 ch, dev;
274
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 */
280 }
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 */
286 }
287 return SCPE_OK;
288 }
289
290 t_stat op_pin (uint32 *dat)
291 {
292 uint32 al = alert; /* local copy */
293
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 */
297 }
298
299 t_stat op_pot (uint32 dat)
300 {
301 uint32 al = alert; /* local copy */
302
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 */
306 }
307
308 /* EOM/EOD */
309
310 t_stat op_eomd (uint32 inst)
311 {
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 # */
316 t_stat r;
317
318 switch (mod) {
319
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 */
329 return r;
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;
333 }
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 */
341 }
342 else return dev_disc (ch, ch_dev); /* disconnect */
343 break;
344
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);
354 }
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);
358 }
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;
364 }
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 */
372 } /* end else TOP */
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 */
379 break;
380
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;
386 set_dyn_map ();
387 }
388 break;
389 } /* end if EOD */
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;
403 }
404 break;
405
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);
410 CRETINS;
411 } /* end case */
412
413 return SCPE_OK;
414 }
415
416 /* Skip if not signal */
417
418 t_stat op_sks (uint32 inst, uint32 *dat)
419 {
420 uint32 mod = I_GETIOMD (inst); /* get mode */
421 uint32 ch = I_GETSKCH (inst); /* get chan # */
422 uint32 dev = inst & DEV_MASK; /* get dev # */
423
424 *dat = 0;
425 if ((ch == 4) && !(inst & 037774)) { /* EM test */
426 if (((inst & 0001) && (EM2 != 2)) ||
427 ((inst & 0002) && (EM3 != 3))) *dat = 1;
428 return SCPE_OK;
429 }
430 switch (mod) {
431
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 */
436 }
437 else { /* channel */
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;
442 }
443 break;
444
445 case 2: /* internal test */
446 if (inst & 0001) { /* test OV */
447 *dat = OV ^ 1; /* skip if off */
448 OV = 0; /* and reset */
449 break;
450 }
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;
461 break;
462
463 case 3: /* special */
464 dev = I_GETDEV3 (inst); /* special device */
465 if (dev3_dsp[dev]) dev3_dsp[dev] (IO_SKS, inst, dat);
466 else CRETINS;
467 } /* end case */
468
469 return SCPE_OK;
470 }
471
472 /* PIN/POT routines */
473
474 t_stat pot_ilc (uint32 num, uint32 *dat)
475 {
476 uint32 ch = num - POT_ILCY;
477
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;
481 return SCPE_OK;
482 }
483
484 t_stat pot_dcr (uint32 num, uint32 *dat)
485 {
486 uint32 ch = num - POT_DCRY;
487
488 chan_dcr[ch] = (*dat) & (CHD_INT | CHD_PAGE);
489 chan_flag[ch] = chan_flag[ch] | CHF_DCHN;
490 return SCPE_OK;
491 }
492
493 t_stat pin_adr (uint32 num, uint32 *dat)
494 {
495 uint32 ch = num - POT_ADRY;
496
497 *dat = chan_mar[ch] & PAMASK;
498 return SCPE_OK;
499 }
500
501 /* System interrupt POT.
502
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 */
507
508 t_stat pot_fork (uint32 num, uint32 *dat)
509 {
510 uint32 igrp = SYI_GETGRP (*dat); /* get group */
511 uint32 fbit = (1 << (VEC_FORK & 017)); /* bit in group */
512
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;
518 }
519 return SCPE_OK;
520 }
521
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
526
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
531 IORD, IORP: ignore
532 IOSD, IOSP: overrun error
533 --wcr == 0: comp: clear interlace
534 IORD, IORP, IOSP: ZWC interrupt
535 IOSD: ZWC interrupt, EOR interrupt, disconnect
536
537 Note that the channel can be disconnected if CHN_EOR is set, but must
538 not be if XFR_REQ is set */
539
540 t_stat chan_read (int32 ch)
541 {
542 uint32 dat = 0;
543 uint32 dev = chan_uar[ch] & DEV_MASK;
544 uint32 tfnc = CHM_GETFNC (chan_mode[ch]);
545 t_stat r = SCPE_OK;
546
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 */
568 } /* end if IOSD */
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];
575 }
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 */
580 } /* end if full */
581 } /* end if xfr */
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 */
587 chan_write_mem (ch);
588 } /* end else if cnt */
589 return chan_eor (ch); /* eot/eor int */
590 }
591 return r;
592 }
593
594 void chan_write_mem (int32 ch)
595 {
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 */
601 return;
602 }
603
604 void chan_flush_war (int32 ch)
605 {
606 int32 i = (chan_cpw[ch] - chan_cnt[ch]) + 1;
607
608 if (i) {
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;
614 }
615 return;
616 }
617
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
622
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
628 IORD, IORP: ignore
629 IOSD: ZWC interrupt, disconnect
630 IOSP: ZWC interrupt, interrecord
631 */
632 t_stat chan_write (int32 ch)
633 {
634 uint32 dat = 0;
635 uint32 dev = chan_uar[ch] & DEV_MASK;
636 uint32 tfnc = CHM_GETFNC (chan_mode[ch]);
637 t_stat r = SCPE_OK;
638
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 */
647 }
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 */
652 return SCPE_OK;
653 } /* end else ilce */
654 } /* end if cnt */
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 */
660 }
661 else { /* 6B */
662 dat = (chan_war[ch] >> 18) & 077; /* get char */
663 chan_war[ch] = (chan_war[ch] << 6) & DMASK; /* remove from war */
664 }
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 */
674 } /* end if comp */
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 */
682 } /* end if SD */
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 */
688 } /* end if wcr */
689 } /* end if ilce */
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 */
693 }
694 else if (ion) int_req = int_req | int_zc[ch]; /* no TOP, EOW intr */
695 } /* end if cnt */
696 } /* end if xfr */
697 if (TST_EOR (ch)) return chan_eor (ch); /* eor rcvd? */
698 return r;
699 }
700
701 /* MAR increment */
702
703 uint32 chan_mar_inc (int32 ch)
704 {
705 uint32 t = (chan_mar[ch] + 1) & PAMASK; /* incr mar */
706
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 */
712 }
713 return t;
714 }
715
716 /* End of record action */
717
718 t_stat chan_eor (int32 ch)
719 {
720 uint32 tfnc = CHM_GETFNC (chan_mode[ch]);
721 uint32 dev = chan_uar[ch] & DEV_MASK;
722
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 */
729 return SCPE_OK;
730 }
731
732 /* Utility routines */
733
734 t_stat dev_disc (uint32 ch, uint32 dev)
735 {
736 chan_uar[ch] = 0; /* disconnect */
737 if (dev_dsp[dev][ch]) return dev_dsp[dev][ch] (IO_DISC, dev, NULL);
738 return SCPE_OK;
739 }
740
741 t_stat dev_wreor (uint32 ch, uint32 dev)
742 {
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 */
745 return SCPE_OK;
746 }
747
748 /* Externally visible routines */
749 /* Channel driver */
750
751 t_stat chan_process (void)
752 {
753 int32 i, dev;
754 t_stat r;
755
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 */
761 if (r) return r;
762 }
763 }
764 return SCPE_OK;
765 }
766
767 /* Test for channel active */
768
769 t_bool chan_testact (void)
770 {
771 int32 i, dev;
772
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;
776 }
777 return 0;
778 }
779
780 /* Async output device ready for more data */
781
782 void chan_set_ordy (int32 ch)
783 {
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 */
789 }
790 return;
791 }
792
793 /* Set flag in channel */
794
795 void chan_set_flag (int32 ch, uint32 fl)
796 {
797 if ((ch >= 0) && (ch < NUM_CHAN)) chan_flag[ch] = chan_flag[ch] | fl;
798 return;
799 }
800
801 /* Set UAR in channel */
802
803 void chan_set_uar (int32 ch, uint32 dev)
804 {
805 if ((ch >= 0) && (ch < NUM_CHAN)) chan_uar[ch] = dev & DEV_MASK;
806 return;
807 }
808
809 /* Disconnect channel */
810
811 void chan_disc (int32 ch)
812 {
813 if ((ch >= 0) && (ch < NUM_CHAN)) chan_uar[ch] = 0;
814 return;
815 }
816
817 /* Reset channels */
818
819 t_stat chan_reset (DEVICE *dptr)
820 {
821 int32 i;
822
823 xfr_req = 0;
824 for (i = 0; i < NUM_CHAN; i++) {
825 chan_uar[i] = 0;
826 chan_wcr[i] = 0;
827 chan_mar[i] = 0;
828 chan_dcr[i] = 0;
829 chan_war[i] = 0;
830 chan_cpw[i] = 0;
831 chan_cnt[i] = 0;
832 chan_mode[i] = 0;
833 chan_flag[i] = 0;
834 }
835 return SCPE_OK;
836 }
837
838 /* Channel assignment routines */
839
840 t_stat set_chan (UNIT *uptr, int32 val, char *sptr, void *desc)
841 {
842 DEVICE *dptr;
843 DIB *dibp;
844 int32 i;
845
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 */
856 return SCPE_OK;
857 }
858 }
859 return SCPE_ARG;
860 }
861
862 t_stat show_chan (FILE *st, UNIT *uptr, int32 val, void *desc)
863 {
864 DEVICE *dptr;
865 DIB *dibp;
866
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]);
873 return SCPE_OK;
874 }
875
876 /* Init device tables */
877
878 t_bool io_init (void)
879 {
880 DEVICE *dptr;
881 DIB *dibp;
882 DSPT *tplp;
883 int32 ch;
884 uint32 i, j, dev, doff;
885
886 /* Clear dispatch table, device map */
887
888 for (i = 0; i < NUM_CHAN; i++) {
889 for (j = 0; j < (DEV_MASK + 1); j++) {
890 dev_dsp[j][i] = NULL;
891 dev_map[j][i] = 0;
892 }
893 }
894
895 /* Test each device for conflict; add to map; init tables */
896
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 */
903 else {
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",
910 chname[ch], doff);
911 if (sim_log) fprintf (sim_log,
912 "Device number conflict, chan = %s, dev = %02o\n",
913 chname[ch], doff);
914 return TRUE;
915 }
916 dev_map[doff][ch] = dibp->xfr; /* set xfr flag */
917 dev_dsp[doff][ch] = dibp->iop; /* set dispatch */
918 } /* end for j */
919 } /* end for tplt */
920 } /* end else */
921 } /* end for i */
922 return FALSE;
923 }
924
925 /* Display channel state */
926
927 t_stat chan_show_reg (FILE *st, UNIT *uptr, int32 val, void *desc)
928 {
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]);
939 return SCPE_OK;
940 }