1 /* sds_io.c: SDS 940 I/O simulator
2
3 Copyright (c) 2001-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 19-Mar-2012 RMS Fixed various declarations (Mark Pizzolato)
27 */
28
29 #include "sds_defs.h"
30
31 /* Data chain word */
32
33 #define CHD_INT 040 /* int on chain */
34 #define CHD_PAGE 037 /* new page # */
35
36 /* Interlace POT */
37
38 #define CHI_V_WC 14 /* word count */
39 #define CHI_M_WC 01777
40 #define CHI_GETWC(x) (((x) >> CHI_V_WC) & CHI_M_WC)
41 #define CHI_V_MA 0 /* mem address */
42 #define CHI_M_MA 037777
43 #define CHI_GETMA(x) (((x) >> CHI_V_MA) & CHI_M_MA)
44
45 /* System interrupt POT */
46
47 #define SYI_V_GRP 18 /* group */
48 #define SYI_M_GRP 077
49 #define SYI_GETGRP(x) (((x) >> SYI_V_GRP) & SYI_M_GRP)
50 #define SYI_DIS (1 << 17) /* disarm if 0 */
51 #define SYI_ARM (1 << 16) /* arm if 1 */
52 #define SYI_M_INT 0177777 /* interrupt */
53
54 /* Pseudo-device number for EOM/SKS mode 3 */
55
56 #define I_GETDEV3(x) ((((x) & 020046000) != 020046000)? ((x) & DEV_MASK): DEV_MASK)
57
58 #define TST_XFR(d,c) (xfr_req && dev_map[d][c])
59 #define SET_XFR(d,c) xfr_req = xfr_req | dev_map[d][c]
60 #define CLR_XFR(d,c) xfr_req = xfr_req & ~dev_map[d][c]
61 #define INV_DEV(d,c) (dev_dsp[d][c] == NULL)
62 #define VLD_DEV(d,c) (dev_dsp[d][c] != NULL)
63 #define TST_EOR(c) (chan_flag[c] & CHF_EOR)
64 #define QAILCE(a) (((a) >= POT_ILCY) && ((a) < (POT_ILCY + NUM_CHAN)))
65
66 uint8 chan_uar[NUM_CHAN]; /* unit addr */
67 uint16 chan_wcr[NUM_CHAN]; /* word count */
68 uint16 chan_mar[NUM_CHAN]; /* mem addr */
69 uint8 chan_dcr[NUM_CHAN]; /* data chain */
70 uint32 chan_war[NUM_CHAN]; /* word assembly */
71 uint8 chan_cpw[NUM_CHAN]; /* char per word */
72 uint8 chan_cnt[NUM_CHAN]; /* char count */
73 uint16 chan_mode[NUM_CHAN]; /* mode */
74 uint16 chan_flag[NUM_CHAN]; /* flags */
75 static const char *chname[NUM_CHAN] = {
76 "W", "Y", "C", "D", "E", "F", "G", "H"
77 };
78
79 extern uint32 M[MAXMEMSIZE]; /* memory */
80 extern uint32 int_req; /* int req */
81 extern uint32 xfr_req; /* xfer req */
82 extern uint32 alert; /* pin/pot alert */
83 extern uint32 X, EM2, EM3, OV, ion, bpt;
84 extern uint32 nml_mode, usr_mode;
85 extern int32 rtc_pie;
86 extern int32 stop_invins, stop_invdev, stop_inviop;
87 extern uint32 mon_usr_trap;
88 extern UNIT cpu_unit;
89 extern FILE *sim_log;
90 extern DEVICE *sim_devices[];
91
92 t_stat chan_reset (DEVICE *dptr);
93 t_stat chan_read (int32 ch);
94 t_stat chan_write (int32 ch);
95 void chan_write_mem (int32 ch);
96 void chan_flush_war (int32 ch);
97 uint32 chan_mar_inc (int32 ch);
98 t_stat chan_eor (int32 ch);
99 t_stat pot_ilc (uint32 num, uint32 *dat);
100 t_stat pot_dcr (uint32 num, uint32 *dat);
101 t_stat pin_adr (uint32 num, uint32 *dat);
102 t_stat pot_fork (uint32 num, uint32 *dat);
103 t_stat dev_disc (uint32 ch, uint32 dev);
104 t_stat dev_wreor (uint32 ch, uint32 dev);
105 extern t_stat pot_RL1 (uint32 num, uint32 *dat);
106 extern t_stat pot_RL2 (uint32 num, uint32 *dat);
107 extern t_stat pot_RL4 (uint32 num, uint32 *dat);
108 extern t_stat pin_rads (uint32 num, uint32 *dat);
109 extern t_stat pot_rada (uint32 num, uint32 *dat);
110 extern t_stat pin_dsk (uint32 num, uint32 *dat);
111 extern t_stat pot_dsk (uint32 num, uint32 *dat);
112 t_stat pin_mux (uint32 num, uint32 *dat);
113 t_stat pot_mux (uint32 num, uint32 *dat);
114 extern void set_dyn_map (void);
115
116 /* SDS I/O model
117
118 A device is modeled by its interactions with a channel. Devices can only be
119 accessed via channels. Each channel has its own device address space. This
120 means devices can only be accessed from a specific channel.
121
122 I/O operations start with a channel connect. The EOM instruction is passed
123 to the device via the conn routine. This routine is also used for non-channel
124 EOM's to the device. For channel connects, the device must remember the
125 channel number.
126
127 The device responds (after a delay) by setting its XFR_RDY flag. This causes
128 the channel to invoke either the read or write routine (for input or output)
129 to get or put the next character. If the device is an asynchronous output
130 device, it calls routine chan_set_ordy to see if there is output available.
131 If there is, XFR_RDY is set; if not, the channel is marked to wake the
132 attached device when output is available. This prevents invalid rate errors.
133
134 Output may be terminated by a write end of record, a disconnect, or both.
135 Write end of record occurs when the word count reaches zero on an IORD or IORP
136 operation. It also occurs if a TOP instruction is issued. The device is
137 expected to respond by setting the end of record indicator in the channel,
138 which will in turn trigger an end of record interrupt.
139
140 When the channel operation completes, the channel disconnects and calls the
141 disconnect processor to perform any device specific cleanup. The differences
142 between write end of record and disconnect are subtle. On magtape output,
143 for example, both signal end of record; but write end of record allows the
144 magtape to continue moving, while disconnect halts its motion.
145
146 Valid devices supply a routine to handle potentially all I/O operations
147 (connect, disconnect, read, write, write end of record, sks). There are
148 separate routines for PIN and POT.
149
150 Channels could, optionally, handle 12b or 24b characters. The simulator can
151 support all widths.
152 */
153
154 t_stat chan_show_reg (FILE *st, UNIT *uptr, int32 val, void *desc);
155
156 struct aldisp {
157 t_stat (*pin) (uint32 num, uint32 *dat); /* altnum, *dat */
158 t_stat (*pot) (uint32 num, uint32 *dat); /* altnum, *dat */
159 };
160
161 /* Channel data structures
162
163 chan_dev channel device descriptor
164 chan_unit channel unit descriptor
165 chan_reg channel register list
166 */
167
168 UNIT chan_unit = { UDATA (NULL, 0, 0) };
169
170 REG chan_reg[] = {
171 { BRDATA (UAR, chan_uar, 8, 6, NUM_CHAN) },
172 { BRDATA (WCR, chan_wcr, 8, 15, NUM_CHAN) },
173 { BRDATA (MAR, chan_mar, 8, 16, NUM_CHAN) },
174 { BRDATA (DCR, chan_dcr, 8, 6, NUM_CHAN) },
175 { BRDATA (WAR, chan_war, 8, 24, NUM_CHAN) },
176 { BRDATA (CPW, chan_cpw, 8, 2, NUM_CHAN) },
177 { BRDATA (CNT, chan_cnt, 8, 3, NUM_CHAN) },
178 { BRDATA (MODE, chan_mode, 8, 12, NUM_CHAN) },
179 { BRDATA (FLAG, chan_flag, 8, CHF_N_FLG, NUM_CHAN) },
180 { NULL }
181 };
182
183 MTAB chan_mod[] = {
184 { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_W, "W", NULL,
185 NULL, &chan_show_reg, NULL },
186 { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_Y, "Y", NULL,
187 NULL, &chan_show_reg, NULL },
188 { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_C, "C", NULL,
189 NULL, &chan_show_reg, NULL },
190 { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_D, "D", NULL,
191 NULL, &chan_show_reg, NULL },
192 { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_E, "E", NULL,
193 NULL, &chan_show_reg, NULL },
194 { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_F, "F", NULL,
195 NULL, &chan_show_reg, NULL },
196 { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_G, "G", NULL,
197 NULL, &chan_show_reg, NULL },
198 { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_H, "H", NULL,
199 NULL, &chan_show_reg, NULL }
200 };
201
202 DEVICE chan_dev = {
203 "CHAN", &chan_unit, chan_reg, chan_mod,
204 1, 8, 8, 1, 8, 8,
205 NULL, NULL, &chan_reset,
206 NULL, NULL, NULL
207 };
208
209 /* Tables */
210
211 static const uint32 int_zc[8] = {
212 INT_WZWC, INT_YZWC, INT_CZWC, INT_DZWC,
213 INT_EZWC, INT_FZWC, INT_GZWC, INT_HZWC
214 };
215
216 static const uint32 int_er[8] = {
217 INT_WEOR, INT_YEOR, INT_CEOR, INT_DEOR,
218 INT_EEOR, INT_FEOR, INT_GEOR, INT_HEOR
219 };
220
221 /* dev_map maps device and channel numbers to a transfer flag masks */
222
223 uint32 dev_map[64][NUM_CHAN];
224
225 /* dev_dsp maps device and channel numbers to dispatch routines */
226
227 t_stat (*dev_dsp[64][NUM_CHAN])() = { NULL };
228
229 /* dev3_dsp maps system device numbers to dispatch routines */
230
231 t_stat (*dev3_dsp[64])() = { NULL };
232
233 /* dev_alt maps alert numbers to dispatch routines */
234
235 struct aldisp dev_alt[] = {
236 { NULL, NULL },
237 { NULL, &pot_ilc }, { NULL, &pot_ilc },
238 { NULL, &pot_ilc }, { NULL, &pot_ilc },
239 { NULL, &pot_ilc }, { NULL, &pot_ilc },
240 { NULL, &pot_ilc }, { NULL, &pot_ilc },
241 { NULL, &pot_dcr }, { NULL, &pot_dcr },
242 { NULL, &pot_dcr }, { NULL, &pot_dcr },
243 { NULL, &pot_dcr }, { NULL, &pot_dcr },
244 { NULL, &pot_dcr }, { NULL, &pot_dcr },
245 { &pin_adr, NULL }, { &pin_adr, NULL },
246 { &pin_adr, NULL }, { &pin_adr, NULL },
247 { &pin_adr, NULL }, { &pin_adr, NULL },
248 { &pin_adr, NULL }, { &pin_adr, NULL },
249 { NULL, &pot_RL1 }, { NULL, &pot_RL2 },
250 { NULL, &pot_RL4 },
251 { &pin_rads, NULL }, { NULL, &pot_rada },
252 { &pin_dsk, &pot_dsk }, { NULL, &pot_fork },
253 { &pin_mux, &pot_mux }
254 };
255
256 /* Single word I/O instructions */
257
op_wyim(uint32 inst,uint32 * dat)258 t_stat op_wyim (uint32 inst, uint32 *dat)
259 {
260 int32 ch, dev;
261
262 ch = (inst & 000200000)? CHAN_W: CHAN_Y; /* get chan# */
263 dev = chan_uar[ch] & DEV_MASK; /* get dev # */
264 if (chan_cnt[ch] <= chan_cpw[ch]) { /* buffer empty? */
265 if (dev == 0) /* no device? dead */
266 return STOP_INVIOP;
267 return STOP_IONRDY; /* hang until full */
268 }
269 *dat = chan_war[ch]; /* get data */
270 chan_war[ch] = 0; /* reset war */
271 chan_cnt[ch] = 0; /* reset cnt */
272 return SCPE_OK;
273 }
274
op_miwy(uint32 inst,uint32 dat)275 t_stat op_miwy (uint32 inst, uint32 dat)
276 {
277 int32 ch, dev;
278
279 ch = (inst & 000200000)? CHAN_W: CHAN_Y; /* get chan# */
280 dev = chan_uar[ch] & DEV_MASK; /* get dev # */
281 if (chan_cnt[ch] != 0) { /* buffer full? */
282 if (dev == 0) /* no device? dead */
283 return STOP_INVIOP;
284 return STOP_IONRDY; /* hang until full */
285 }
286 chan_war[ch] = dat; /* get data */
287 chan_cnt[ch] = chan_cpw[ch] + 1; /* buffer full */
288 if (chan_flag[ch] & CHF_OWAK) { /* output wake? */
289 if (VLD_DEV (dev, ch)) /* wake channel */
290 SET_XFR (dev, ch);
291 chan_flag[ch] = chan_flag[ch] & ~CHF_OWAK; /* clear wake */
292 }
293 return SCPE_OK;
294 }
295
op_pin(uint32 * dat)296 t_stat op_pin (uint32 *dat)
297 {
298 uint32 al = alert; /* local copy */
299
300 alert = 0; /* clear alert */
301 if ((al == 0) || (dev_alt[al].pin == NULL)) /* inv alert? */
302 CRETIOP;
303 return dev_alt[al].pin (al, dat); /* PIN from dev */
304 }
305
op_pot(uint32 dat)306 t_stat op_pot (uint32 dat)
307 {
308 uint32 al = alert; /* local copy */
309
310 alert = 0; /* clear alert */
311 if ((al == 0) || (dev_alt[al].pot == NULL)) /* inv alert? */
312 CRETIOP;
313 return dev_alt[al].pot (al, &dat); /* POT to dev */
314 }
315
316 /* EOM/EOD */
317
op_eomd(uint32 inst)318 t_stat op_eomd (uint32 inst)
319 {
320 uint32 mod = I_GETIOMD (inst); /* get mode */
321 uint32 ch = I_GETEOCH (inst); /* get chan # */
322 uint32 dev = inst & DEV_MASK; /* get dev # */
323 uint32 ch_dev = chan_uar[ch] & DEV_MASK; /* chan curr dev # */
324 t_stat r;
325
326 switch (mod) {
327
328 case 0: /* IO control */
329 if (dev) { /* new dev? */
330 if (ch_dev) /* chan act? err */
331 CRETIOP;
332 if (INV_DEV (dev, ch)) /* inv dev? err */
333 CRETDEV;
334 chan_war[ch] = chan_cnt[ch] = 0; /* init chan */
335 chan_flag[ch] = chan_dcr[ch] = 0;
336 chan_mode[ch] = chan_uar[ch] = 0;
337 if (ch >= CHAN_E)
338 chan_mode[ch] = CHM_CE;
339 if ((r = dev_dsp[dev][ch] (IO_CONN, inst, NULL))) /* connect */
340 return r;
341 if ((inst & I_IND) || (ch >= CHAN_C)) { /* C-H? alert ilc */
342 alert = POT_ILCY + ch;
343 chan_mar[ch] = chan_wcr[ch] = 0;
344 }
345 if (chan_flag[ch] & CHF_24B) /* 24B? 1 ch/wd */
346 chan_cpw[ch] = 0;
347 else if (chan_flag[ch] & CHF_12B) /* 12B? 2 ch/wd */
348 chan_cpw[ch] = CHC_GETCPW (inst) & 1;
349 else chan_cpw[ch] = CHC_GETCPW (inst); /* 6b, 1-4 ch/wd */
350 chan_uar[ch] = dev; /* connected */
351 if ((dev & DEV_OUT) && ion && !QAILCE (alert)) /* out, prog IO? */
352 int_req = int_req | int_zc[ch]; /* initial intr */
353 }
354 else return dev_disc (ch, ch_dev); /* disconnect */
355 break;
356
357 case 1: /* buf control */
358 if (QAILCE (alert)) { /* ilce alerted? */
359 ch = alert - POT_ILCY; /* derive chan */
360 if (ch >= CHAN_E) /* DACC? ext */
361 inst = inst | CHM_CE;
362 chan_mode[ch] = inst; /* save mode */
363 chan_mar[ch] = (CHM_GETHMA (inst) << 14) | /* get hi mar */
364 (chan_mar[ch] & CHI_M_MA);
365 chan_wcr[ch] = (CHM_GETHWC (inst) << 10) | /* get hi wc */
366 (chan_wcr[ch] & CHI_M_WC);
367 }
368 else if (dev) { /* dev EOM */
369 if (INV_DEV (dev, ch)) /* inv dev? err */
370 CRETDEV;
371 return dev_dsp[dev][ch] (IO_EOM1, inst, NULL);
372 }
373 else { /* chan EOM */
374 inst = inst & 047677;
375 if (inst == 040000) { /* alert ilce */
376 alert = POT_ILCY + ch;
377 chan_mar[ch] = chan_wcr[ch] = 0;
378 }
379 else if (inst == 002000) /* alert addr */
380 alert = POT_ADRY + ch;
381 else if (inst == 001000) /* alert DCR */
382 alert = POT_DCRY + ch;
383 else if (inst == 004000) { /* term output */
384 if (ch_dev & DEV_OUT) { /* to output dev? */
385 if (chan_cnt[ch] || (chan_flag[ch] & CHF_ILCE)) /* busy, DMA? */
386 chan_flag[ch] = chan_flag[ch] | CHF_TOP; /* TOP pending */
387 else return dev_wreor (ch, ch_dev); /* idle, write EOR */
388 } /* end else TOP */
389 else if (ch_dev & DEV_MT) { /* change to scan? */
390 chan_uar[ch] = chan_uar[ch] | DEV_MTS; /* change dev addr */
391 chan_flag[ch] = chan_flag[ch] | CHF_SCAN; /* set scan flag */
392 } /* end else change scan */
393 } /* end else term output */
394 } /* end else chan EOM */
395 break;
396
397 case 2: /* internal */
398 if (ch >= CHAN_E) { /* EOD? */
399 if (inst & 00300) { /* set EM? */
400 if (inst & 00100)
401 EM2 = inst & 07;
402 if (inst & 00200)
403 EM3 = (inst >> 3) & 07;
404 set_dyn_map ();
405 }
406 break;
407 } /* end if EOD */
408 if (inst & 00001) /* clr OV */
409 OV = 0;
410 if (inst & 00002) /* ion */
411 ion = 1;
412 else if (inst & 00004) /* iof */
413 ion = 0;
414 if ((inst & 00010) && (((X >> 1) ^ X) & EXPS))
415 OV = 1;
416 if (inst & 00020) /* alert sys int */
417 alert = POT_SYSI;
418 if (inst & 00100) /* arm clk pls */
419 rtc_pie = 1;
420 else if (inst & 00200) /* disarm pls */
421 rtc_pie = 0;
422 if ((inst & 01400) == 01400) /* alert RL4 */
423 alert = POT_RL4;
424 else if (inst & 00400) /* alert RL1 */
425 alert = POT_RL1;
426 else if (inst & 01000) /* alert RL2 */
427 alert = POT_RL2;
428 if (inst & 02000) { /* nml to mon */
429 nml_mode = usr_mode = 0;
430 if (inst & 00400)
431 mon_usr_trap = 1;
432 }
433 break;
434
435 case 3: /* special */
436 dev = I_GETDEV3 (inst); /* special device */
437 if (dev3_dsp[dev]) /* defined? */
438 return dev3_dsp[dev] (IO_CONN, inst, NULL);
439 CRETINS;
440 } /* end case */
441
442 return SCPE_OK;
443 }
444
445 /* Skip if not signal */
446
op_sks(uint32 inst,uint32 * dat)447 t_stat op_sks (uint32 inst, uint32 *dat)
448 {
449 uint32 mod = I_GETIOMD (inst); /* get mode */
450 uint32 ch = I_GETSKCH (inst); /* get chan # */
451 uint32 dev = inst & DEV_MASK; /* get dev # */
452
453 *dat = 0;
454 if ((ch == 4) && !(inst & 037774)) { /* EM test */
455 if (((inst & 0001) && (EM2 != 2)) ||
456 ((inst & 0002) && (EM3 != 3)))
457 *dat = 1;
458 return SCPE_OK;
459 }
460 switch (mod) {
461
462 case 1: /* ch, dev */
463 if (dev) { /* device */
464 if (INV_DEV (dev, ch)) /* inv dev? err */
465 CRETDEV;
466 dev_dsp[dev][ch] (IO_SKS, inst, dat); /* do test */
467 }
468 else { /* channel */
469 if (((inst & 04000) && (chan_uar[ch] == 0)) ||
470 ((inst & 02000) && (chan_wcr[ch] == 0)) ||
471 ((inst & 01000) && ((chan_flag[ch] & CHF_ERR) == 0)) ||
472 ((inst & 00400) && (chan_flag[ch] & CHF_IREC)))
473 *dat = 1;
474 }
475 break;
476
477 case 2: /* internal test */
478 if (inst & 0001) { /* test OV */
479 *dat = OV ^ 1; /* skip if off */
480 OV = 0; /* and reset */
481 break;
482 }
483 if (((inst & 00002) && !ion) || /* ion, bpt test */
484 ((inst & 00004) && ion) ||
485 ((inst & 00010) && ((chan_flag[CHAN_W] & CHF_ERR) == 0)) ||
486 ((inst & 00020) && ((chan_flag[CHAN_Y] & CHF_ERR) == 0)) ||
487 ((inst & 00040) && ((bpt & 001) == 0)) ||
488 ((inst & 00100) && ((bpt & 002) == 0)) ||
489 ((inst & 00200) && ((bpt & 004) == 0)) ||
490 ((inst & 00400) && ((bpt & 010) == 0)) ||
491 ((inst & 01000) && (chan_uar[CHAN_W] == 0)) ||
492 ((inst & 02000) && (chan_uar[CHAN_Y] == 0)))
493 *dat = 1;
494 break;
495
496 case 3: /* special */
497 dev = I_GETDEV3 (inst); /* special device */
498 if (dev3_dsp[dev])
499 dev3_dsp[dev] (IO_SKS, inst, dat);
500 else CRETINS;
501 } /* end case */
502
503 return SCPE_OK;
504 }
505
506 /* PIN/POT routines */
507
pot_ilc(uint32 num,uint32 * dat)508 t_stat pot_ilc (uint32 num, uint32 *dat)
509 {
510 uint32 ch = num - POT_ILCY;
511
512 chan_mar[ch] = (chan_mar[ch] & ~CHI_M_MA) | CHI_GETMA (*dat);
513 chan_wcr[ch] = (chan_wcr[ch] & ~CHI_M_WC) | CHI_GETWC (*dat);
514 chan_flag[ch] = chan_flag[ch] | CHF_ILCE;
515 return SCPE_OK;
516 }
517
pot_dcr(uint32 num,uint32 * dat)518 t_stat pot_dcr (uint32 num, uint32 *dat)
519 {
520 uint32 ch = num - POT_DCRY;
521
522 chan_dcr[ch] = (*dat) & (CHD_INT | CHD_PAGE);
523 chan_flag[ch] = chan_flag[ch] | CHF_DCHN;
524 return SCPE_OK;
525 }
526
pin_adr(uint32 num,uint32 * dat)527 t_stat pin_adr (uint32 num, uint32 *dat)
528 {
529 uint32 ch = num - POT_ADRY;
530
531 *dat = chan_mar[ch] & PAMASK;
532 return SCPE_OK;
533 }
534
535 /* System interrupt POT.
536
537 The SDS 940 timesharing system uses a permanently asserted
538 system interrupt as a way of forking the teletype input
539 interrupt handler to a lower priority. The interrupt is
540 armed to set up the fork, and disarmed in the fork routine */
541
pot_fork(uint32 num,uint32 * dat)542 t_stat pot_fork (uint32 num, uint32 *dat)
543 {
544 uint32 igrp = SYI_GETGRP (*dat); /* get group */
545 uint32 fbit = (1 << (VEC_FORK & 017)); /* bit in group */
546
547 if (igrp == (VEC_FORK / 020)) { /* right group? */
548 if ((*dat & SYI_ARM) && (*dat & fbit)) /* arm, bit set? */
549 int_req = int_req | INT_FORK;
550 if ((*dat & SYI_DIS) && !(*dat & fbit)) /* disarm, bit clr? */
551 int_req = int_req & ~INT_FORK;
552 }
553 return SCPE_OK;
554 }
555
556 /* Channel read invokes the I/O device to get the next character and,
557 if not end of record, assembles it into the word assembly register.
558 If the interlace is on, the full word is stored in memory.
559 The key difference points for the various terminal functions are
560
561 end of record comp: EOT interrupt
562 IORD, IOSD: EOR interrupt, disconnect
563 IORP, IOSP: EOR interrupt, interrecord
564 interlace off: comp: EOW interrupt
565 IORD, IORP: ignore
566 IOSD, IOSP: overrun error
567 --wcr == 0: comp: clear interlace
568 IORD, IORP, IOSP: ZWC interrupt
569 IOSD: ZWC interrupt, EOR interrupt, disconnect
570
571 Note that the channel can be disconnected if CHN_EOR is set, but must
572 not be if XFR_REQ is set */
573
chan_read(int32 ch)574 t_stat chan_read (int32 ch)
575 {
576 uint32 dat = 0;
577 uint32 dev = chan_uar[ch] & DEV_MASK;
578 uint32 tfnc = CHM_GETFNC (chan_mode[ch]);
579 t_stat r = SCPE_OK;
580
581 if (dev && TST_XFR (dev, ch)) { /* ready to xfr? */
582 if (INV_DEV (dev, ch)) CRETIOP; /* can't read? */
583 r = dev_dsp[dev][ch] (IO_READ, dev, &dat); /* read data */
584 if (r) /* error? */
585 chan_flag[ch] = chan_flag[ch] | CHF_ERR;
586 if (chan_flag[ch] & CHF_24B) /* 24B? */
587 chan_war[ch] = dat;
588 else if (chan_flag[ch] & CHF_12B) /* 12B? */
589 chan_war[ch] = ((chan_war[ch] << 12) | (dat & 07777)) & DMASK;
590 else chan_war[ch] = ((chan_war[ch] << 6) | (dat & 077)) & DMASK;
591 if (chan_flag[ch] & CHF_SCAN) /* scanning? */
592 chan_cnt[ch] = chan_cpw[ch]; /* never full */
593 else chan_cnt[ch] = chan_cnt[ch] + 1; /* insert char */
594 if (chan_cnt[ch] > chan_cpw[ch]) { /* full? */
595 if (chan_flag[ch] & CHF_ILCE) { /* interlace on? */
596 chan_write_mem (ch); /* write to mem */
597 if (chan_wcr[ch] == 0) { /* wc zero? */
598 chan_flag[ch] = chan_flag[ch] & ~CHF_ILCE; /* clr interlace */
599 if ((tfnc != CHM_COMP) && (chan_mode[ch] & CHM_ZC))
600 int_req = int_req | int_zc[ch]; /* zwc interrupt */
601 if (tfnc == CHM_IOSD) { /* IOSD? also EOR */
602 if (chan_mode[ch] & CHM_ER)
603 int_req = int_req | int_er[ch];
604 dev_disc (ch, dev); /* disconnect */
605 } /* end if IOSD */
606 } /* end if wcr == 0 */
607 } /* end if ilce on */
608 else { /* interlace off */
609 if (TST_EOR (ch)) /* eor? */
610 return chan_eor (ch);
611 if (tfnc == CHM_COMP) { /* C: EOW, intr */
612 if (ion)
613 int_req = int_req | int_zc[ch];
614 }
615 else if (tfnc & CHM_SGNL) /* Sx: error */
616 chan_flag[ch] = chan_flag[ch] | CHF_ERR;
617 else chan_cnt[ch] = chan_cpw[ch]; /* Rx: ignore */
618 } /* end else ilce */
619 } /* end if full */
620 } /* end if xfr */
621 if (TST_EOR (ch)) { /* end record? */
622 if (tfnc == CHM_COMP) /* C: fill war */
623 chan_flush_war (ch);
624 else if (chan_cnt[ch]) { /* RX, CX: fill? */
625 chan_flush_war (ch); /* fill war */
626 if (chan_flag[ch] & CHF_ILCE) /* ilce on? store */
627 chan_write_mem (ch);
628 } /* end else if cnt */
629 return chan_eor (ch); /* eot/eor int */
630 }
631 return r;
632 }
633
chan_write_mem(int32 ch)634 void chan_write_mem (int32 ch)
635 {
636 WriteP (chan_mar[ch], chan_war[ch]); /* write to mem */
637 chan_mar[ch] = chan_mar_inc (ch); /* incr mar */
638 chan_wcr[ch] = (chan_wcr[ch] - 1) & 077777; /* decr wcr */
639 chan_war[ch] = 0; /* reset war */
640 chan_cnt[ch] = 0; /* reset cnt */
641 return;
642 }
643
chan_flush_war(int32 ch)644 void chan_flush_war (int32 ch)
645 {
646 int32 i = (chan_cpw[ch] - chan_cnt[ch]) + 1;
647
648 if (i) {
649 if (chan_flag[ch] & CHF_24B)
650 chan_war[ch] = 0;
651 else if (chan_flag[ch] & CHF_12B)
652 chan_war[ch] = (chan_war[ch] << 12) & DMASK;
653 else chan_war[ch] = (chan_war[ch] << (i * 6)) & DMASK;
654 chan_cnt[ch] = chan_cpw[ch] + 1;
655 }
656 return;
657 }
658
659 /* Channel write gets the next character and sends it to the I/O device.
660 If this is the last character in an interlace operation, the end of
661 record operation is invoked.
662 The key difference points for the various terminal functions are
663
664 end of record: comp: EOT interrupt
665 IORD, IOSD: EOR interrupt, disconnect
666 IORP, IOSP: EOR interrupt, interrecord
667 interlace off: if not end of record, EOW interrupt
668 --wcr == 0: comp: EOT interrupt, disconnect
669 IORD, IORP: ignore
670 IOSD: ZWC interrupt, disconnect
671 IOSP: ZWC interrupt, interrecord
672 */
chan_write(int32 ch)673 t_stat chan_write (int32 ch)
674 {
675 uint32 dat = 0;
676 uint32 dev = chan_uar[ch] & DEV_MASK;
677 uint32 tfnc = CHM_GETFNC (chan_mode[ch]);
678 t_stat r = SCPE_OK;
679
680 if (dev && TST_XFR (dev, ch)) { /* ready to xfr? */
681 if (INV_DEV (dev, ch)) /* invalid dev? */
682 CRETIOP;
683 if (chan_cnt[ch] == 0) { /* buffer empty? */
684 if (chan_flag[ch] & CHF_ILCE) { /* interlace on? */
685 chan_war[ch] = ReadP (chan_mar[ch]);
686 chan_mar[ch] = chan_mar_inc (ch); /* incr mar */
687 chan_wcr[ch] = (chan_wcr[ch] - 1) & 077777; /* decr mar */
688 chan_cnt[ch] = chan_cpw[ch] + 1; /* set cnt */
689 }
690 else { /* ilce off */
691 CLR_XFR (dev, ch); /* cant xfr */
692 if (TST_EOR (dev)) /* EOR? */
693 return chan_eor (ch);
694 chan_flag[ch] = chan_flag[ch] | CHF_ERR; /* rate err */
695 return SCPE_OK;
696 } /* end else ilce */
697 } /* end if cnt */
698 chan_cnt[ch] = chan_cnt[ch] - 1; /* decr cnt */
699 if (chan_flag[ch] & CHF_24B) /* 24B? */
700 dat = chan_war[ch];
701 else if (chan_flag[ch] & CHF_12B) { /* 12B? */
702 dat = (chan_war[ch] >> 12) & 07777; /* get halfword */
703 chan_war[ch] = (chan_war[ch] << 12) & DMASK; /* remove from war */
704 }
705 else { /* 6B */
706 dat = (chan_war[ch] >> 18) & 077; /* get char */
707 chan_war[ch] = (chan_war[ch] << 6) & DMASK; /* remove from war */
708 }
709 r = dev_dsp[dev][ch] (IO_WRITE, dev, &dat); /* write */
710 if (r) /* error? */
711 chan_flag[ch] = chan_flag[ch] | CHF_ERR;
712 if (chan_cnt[ch] == 0) { /* buf empty? */
713 if (chan_flag[ch] & CHF_ILCE) { /* ilce on? */
714 if (chan_wcr[ch] == 0) { /* wc now 0? */
715 chan_flag[ch] = chan_flag[ch] & ~CHF_ILCE; /* ilc off */
716 if (tfnc == CHM_COMP) { /* compatible? */
717 if (ion)
718 int_req = int_req | int_zc[ch];
719 dev_disc (ch, dev); /* disconnnect */
720 } /* end if comp */
721 else { /* extended */
722 if (chan_mode[ch] & CHM_ZC) /* ZWC int */
723 int_req = int_req | int_zc[ch];
724 if (tfnc == CHM_IOSD) { /* SD */
725 if (chan_mode[ch] & CHM_ER) /* EOR int */
726 int_req = int_req | int_er[ch];
727 dev_disc (ch, dev); /* disconnnect */
728 } /* end if SD */
729 else if (!(tfnc && CHM_SGNL) || /* IORx or IOSP TOP? */
730 (chan_flag[ch] & CHF_TOP))
731 dev_wreor (ch, dev); /* R: write EOR */
732 chan_flag[ch] = chan_flag[ch] & ~CHF_TOP;
733 } /* end else comp */
734 } /* end if wcr */
735 } /* end if ilce */
736 else if (chan_flag[ch] & CHF_TOP) { /* off, TOP pending? */
737 chan_flag[ch] = chan_flag[ch] & ~CHF_TOP; /* clear TOP */
738 dev_wreor (ch, dev); /* write EOR */
739 }
740 else if (ion) /* no TOP, EOW intr */
741 int_req = int_req | int_zc[ch];
742 } /* end if cnt */
743 } /* end if xfr */
744 if (TST_EOR (ch)) /* eor rcvd? */
745 return chan_eor (ch);
746 return r;
747 }
748
749 /* MAR increment */
750
chan_mar_inc(int32 ch)751 uint32 chan_mar_inc (int32 ch)
752 {
753 uint32 t = (chan_mar[ch] + 1) & PAMASK; /* incr mar */
754
755 if ((chan_flag[ch] & CHF_DCHN) && ((t & VA_POFF) == 0)) { /* chain? */
756 chan_flag[ch] = chan_flag[ch] & ~CHF_DCHN; /* clr flag */
757 if (chan_dcr[ch] & CHD_INT) /* if armed, intr */
758 int_req = int_req | int_zc[ch];
759 t = (chan_dcr[ch] & CHD_PAGE) << VA_V_PN; /* new mar */
760 }
761 return t;
762 }
763
764 /* End of record action */
765
chan_eor(int32 ch)766 t_stat chan_eor (int32 ch)
767 {
768 uint32 tfnc = CHM_GETFNC (chan_mode[ch]);
769 uint32 dev = chan_uar[ch] & DEV_MASK;
770
771 chan_flag[ch] = chan_flag[ch] & ~(CHF_EOR | CHF_ILCE); /* clr eor, ilce */
772 if (((tfnc == CHM_COMP) && ion) || (chan_mode[ch] & CHM_ER))
773 int_req = int_req | int_er[ch]; /* EOT/EOR? */
774 if (dev && (tfnc & CHM_PROC)) /* P, still conn? */
775 chan_flag[ch] = chan_flag[ch] | CHF_IREC; /* interrecord */
776 else return dev_disc (ch, dev); /* disconnect */
777 return SCPE_OK;
778 }
779
780 /* Utility routines */
781
dev_disc(uint32 ch,uint32 dev)782 t_stat dev_disc (uint32 ch, uint32 dev)
783 {
784 chan_uar[ch] = 0; /* disconnect */
785 if (dev_dsp[dev][ch])
786 return dev_dsp[dev][ch] (IO_DISC, dev, NULL);
787 return SCPE_OK;
788 }
789
dev_wreor(uint32 ch,uint32 dev)790 t_stat dev_wreor (uint32 ch, uint32 dev)
791 {
792 if (dev_dsp[dev][ch])
793 return dev_dsp[dev][ch] (IO_WREOR, dev, NULL);
794 chan_flag[ch] = chan_flag[ch] | CHF_EOR; /* set eor */
795 return SCPE_OK;
796 }
797
798 /* Externally visible routines */
799 /* Channel driver */
800
chan_process(void)801 t_stat chan_process (void)
802 {
803 int32 i, dev;
804 t_stat r;
805
806 for (i = 0; i < NUM_CHAN; i++) { /* loop thru */
807 dev = chan_uar[i] & DEV_MASK; /* get dev */
808 if ((dev && TST_XFR (dev, i)) || TST_EOR (i)) { /* chan active? */
809 if (dev & DEV_OUT) /* write */
810 r = chan_write (i);
811 else r = chan_read (i); /* read */
812 if (r)
813 return r;
814 }
815 }
816 return SCPE_OK;
817 }
818
819 /* Test for channel active */
820
chan_testact(void)821 t_bool chan_testact (void)
822 {
823 int32 i, dev;
824
825 for (i = 0; i < NUM_CHAN; i++) {
826 dev = chan_uar[i] & DEV_MASK;
827 if ((dev && TST_XFR (dev, i)) || TST_EOR (i))
828 return 1;
829 }
830 return 0;
831 }
832
833 /* Async output device ready for more data */
834
chan_set_ordy(int32 ch)835 void chan_set_ordy (int32 ch)
836 {
837 if ((ch >= 0) && (ch < NUM_CHAN)) {
838 int32 dev = chan_uar[ch] & DEV_MASK; /* get dev */
839 if (chan_cnt[ch] || (chan_flag[ch] & CHF_ILCE)) /* buf or ilce? */
840 SET_XFR (dev, ch); /* set xfr flg */
841 else chan_flag[ch] = chan_flag[ch] | CHF_OWAK; /* need wakeup */
842 }
843 return;
844 }
845
846 /* Set flag in channel */
847
chan_set_flag(int32 ch,uint32 fl)848 void chan_set_flag (int32 ch, uint32 fl)
849 {
850 if ((ch >= 0) && (ch < NUM_CHAN))
851 chan_flag[ch] = chan_flag[ch] | fl;
852 return;
853 }
854
855 /* Set UAR in channel */
856
chan_set_uar(int32 ch,uint32 dev)857 void chan_set_uar (int32 ch, uint32 dev)
858 {
859 if ((ch >= 0) && (ch < NUM_CHAN))
860 chan_uar[ch] = dev & DEV_MASK;
861 return;
862 }
863
864 /* Disconnect channel */
865
chan_disc(int32 ch)866 void chan_disc (int32 ch)
867 {
868 if ((ch >= 0) && (ch < NUM_CHAN))
869 chan_uar[ch] = 0;
870 return;
871 }
872
873 /* Reset channels */
874
chan_reset(DEVICE * dptr)875 t_stat chan_reset (DEVICE *dptr)
876 {
877 int32 i;
878
879 xfr_req = 0;
880 for (i = 0; i < NUM_CHAN; i++) {
881 chan_uar[i] = 0;
882 chan_wcr[i] = 0;
883 chan_mar[i] = 0;
884 chan_dcr[i] = 0;
885 chan_war[i] = 0;
886 chan_cpw[i] = 0;
887 chan_cnt[i] = 0;
888 chan_mode[i] = 0;
889 chan_flag[i] = 0;
890 }
891 return SCPE_OK;
892 }
893
894 /* Channel assignment routines */
895
set_chan(UNIT * uptr,int32 val,char * sptr,void * desc)896 t_stat set_chan (UNIT *uptr, int32 val, char *sptr, void *desc)
897 {
898 DEVICE *dptr;
899 DIB *dibp;
900 int32 i;
901
902 if (sptr == NULL) /* valid args? */
903 return SCPE_ARG;
904 if (uptr == NULL)
905 return SCPE_IERR;
906 dptr = find_dev_from_unit (uptr);
907 if (dptr == NULL)
908 return SCPE_IERR;
909 dibp = (DIB *) dptr->ctxt;
910 if (dibp == NULL)
911 return SCPE_IERR;
912 for (i = 0; i < NUM_CHAN; i++) { /* match input */
913 if (strcmp (sptr, chname[i]) == 0) { /* find string */
914 if (val && !(val & (1 << i))) /* legal? */
915 return SCPE_ARG;
916 dibp->chan = i; /* store new */
917 return SCPE_OK;
918 }
919 }
920 return SCPE_ARG;
921 }
922
show_chan(FILE * st,UNIT * uptr,int32 val,void * desc)923 t_stat show_chan (FILE *st, UNIT *uptr, int32 val, void *desc)
924 {
925 DEVICE *dptr;
926 DIB *dibp;
927
928 if (uptr == NULL)
929 return SCPE_IERR;
930 dptr = find_dev_from_unit (uptr);
931 if (dptr == NULL)
932 return SCPE_IERR;
933 dibp = (DIB *) dptr->ctxt;
934 if (dibp == NULL)
935 return SCPE_IERR;
936 fprintf (st, "channel=%s", chname[dibp->chan]);
937 return SCPE_OK;
938 }
939
940 /* Init device tables */
941
io_init(void)942 t_bool io_init (void)
943 {
944 DEVICE *dptr;
945 DIB *dibp;
946 DSPT *tplp;
947 int32 ch;
948 uint32 i, j, dev, doff;
949
950 /* Clear dispatch table, device map */
951
952 for (i = 0; i < NUM_CHAN; i++) {
953 for (j = 0; j < (DEV_MASK + 1); j++) {
954 dev_dsp[j][i] = NULL;
955 dev_map[j][i] = 0;
956 }
957 }
958
959 /* Test each device for conflict; add to map; init tables */
960
961 for (i = 0; (dptr = sim_devices[i]); i++) { /* loop thru devices */
962 dibp = (DIB *) dptr->ctxt; /* get DIB */
963 if ((dibp == NULL) || (dptr->flags & DEV_DIS)) /* exist, enabled? */
964 continue;
965 ch = dibp->chan; /* get channel */
966 dev = dibp->dev; /* get device num */
967 if (ch < 0) /* special device */
968 dev3_dsp[dev] = dibp->iop;
969 else {
970 if (dibp->tplt == NULL) /* must have template */
971 return TRUE;
972 for (tplp = dibp->tplt; tplp->num; tplp++) { /* loop thru templates */
973 for (j = 0; j < tplp->num; j++) { /* repeat as needed */
974 doff = dev + tplp->off + j; /* get offset dnum */
975 if (dev_map[doff][ch]) { /* slot in use? */
976 printf ("Device number conflict, chan = %s, devno = %02o\n",
977 chname[ch], doff);
978 if (sim_log)
979 fprintf (sim_log, "Device number conflict, chan = %s, dev = %02o\n",
980 chname[ch], doff);
981 return TRUE;
982 }
983 dev_map[doff][ch] = dibp->xfr; /* set xfr flag */
984 dev_dsp[doff][ch] = dibp->iop; /* set dispatch */
985 } /* end for j */
986 } /* end for tplt */
987 } /* end else */
988 } /* end for i */
989 return FALSE;
990 }
991
992 /* Display channel state */
993
chan_show_reg(FILE * st,UNIT * uptr,int32 val,void * desc)994 t_stat chan_show_reg (FILE *st, UNIT *uptr, int32 val, void *desc)
995 {
996 if ((val < 0) || (val >= NUM_CHAN)) return SCPE_IERR;
997 fprintf (st, "UAR: %02o\n", chan_uar[val]);
998 fprintf (st, "WCR: %05o\n", chan_wcr[val]);
999 fprintf (st, "MAR: %06o\n", chan_mar[val]);
1000 fprintf (st, "DCR: %02o\n", chan_dcr[val]);
1001 fprintf (st, "WAR: %08o\n", chan_war[val]);
1002 fprintf (st, "CPW: %o\n", chan_cpw[val]);
1003 fprintf (st, "CNT: %o\n", chan_cnt[val]);
1004 fprintf (st, "MODE: %03o\n", chan_mode[val]);
1005 fprintf (st, "FLAG: %04o\n", chan_flag[val]);
1006 return SCPE_OK;
1007 }
1008