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