1 /* id_pas.c: Interdata programmable async line adapter 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    pas          Programmable asynchronous line adapter(s)
27 
28    18-Apr-12    RMS     Revised to use clock coscheduling
29    21-Mar-12    RMS     Fixed TT_GET_MODE test to use TTUF_MODE_x (Michael Bloom)
30    19-Nov-08    RMS     Revised for common TMXR show routines
31    18-Jun-07    RMS     Added UNIT_IDLE flag
32    18-Oct-06    RMS     Synced PASLA to clock
33    22-Nov-05    RMS     Revised for new terminal processing routines
34    29-Jun-05    RMS     Added SET PASLn DISCONNECT
35    21-Jun-05    RMS     Fixed bug in SHOW CONN/STATS
36    05-Jan-04    RMS     Revised for tmxr library changes
37    09-May-03    RMS     Added network device flag
38 
39    This module implements up to 32 individual serial interfaces, representing
40    either individual PASLA modules or combinations of the 2-line and 8-line
41    multiplexors, which are functionally very similar. These interfaces are mapped
42    to Telnet based connections as the lines of a terminal multiplexor.  The
43    connection polling mechanism and the character input polling for all lines
44    are done through a single polling job.
45 */
46 
47 #include "id_defs.h"
48 #include "sim_sock.h"
49 #include "sim_tmxr.h"
50 #include <ctype.h>
51 
52 #define PAS_LINES       32
53 
54 #define UNIT_V_MDM      (TTUF_V_UF + 0)                 /* modem control */
55 #define UNIT_MDM        (1 << UNIT_V_MDM)
56 
57 #define PASL_WAIT       500
58 
59 /* Status byte */
60 
61 #define STA_OVR         0x80                            /* overrun RO */
62 #define STA_PF          0x40                            /* parity err RONI */
63 #define STA_NCL2S       0x40                            /* not clr to snd XO */
64 #define STA_FR          0x20                            /* framing err RO */
65 #define STA_RCR         0x10                            /* rv chan rcv NI */
66 #define STA_CROF        0x02                            /* carrier off RO */
67 #define STA_RING        0x01                            /* ring RO */
68 #define STA_RCV         (STA_OVR|STA_PF|STA_FR|STA_RCR|STA_CROF|STA_RING)
69 #define SET_EX          (STA_OVR|STA_PF|STA_FR)
70 #define STA_XMT         (STA_BSY)
71 
72 /* Command bytes 1,0 */
73 
74 #define CMD_DTR         (0x20 << 8)                     /* DTR */
75 #define CMD_ECHO        (0x10 << 8)                     /* echoplex */
76 #define CMD_RCT         (0x08 << 8)                     /* RCT/DTB NI */
77 #define CMD_XMTB        (0x04 << 8)                     /* xmt break NI */
78 #define CMD_WRT         (0x02 << 8)                     /* write/read */
79 #define CMD_V_CLK       6                               /* baud rate */
80 #define CMD_M_CLK       0x3
81 #define CMD_V_DB        4                               /* data bits */
82 #define CMD_M_DB        0x3
83 #define CMD_STOP        0x80                            /* stop bit */
84 #define CMD_V_PAR       1                               /* parity */
85 #define CMD_M_PAR       0x3
86 #define GET_PAR(x)      (((x) >> CMD_V_PAR) & CMD_M_PAR)
87 #define PAR_NONE        0
88 #define PAR_RAW         1
89 #define PAR_ODD         2
90 #define PAR_EVEN        3
91 
92 #define CMD_TYP         0x01                            /* command type */
93 
94 extern uint32 int_req[INTSZ], int_enb[INTSZ];
95 extern int32 lfc_poll;
96 
97 uint8 pas_sta[PAS_LINES];                               /* status */
98 uint16 pas_cmd[PAS_LINES];                              /* command */
99 uint8 pas_rbuf[PAS_LINES];                              /* rcv buf */
100 uint8 pas_xbuf[PAS_LINES];                              /* xmt buf */
101 uint8 pas_rarm[PAS_LINES];                              /* rcvr int armed */
102 uint8 pas_xarm[PAS_LINES];                              /* xmt int armed */
103 uint8 pas_rchp[PAS_LINES];                              /* rcvr chr pend */
104 uint8 pas_tplte[PAS_LINES * 2 + 1];                     /* template */
105 
106 TMLN pas_ldsc[PAS_LINES] = { 0 };                       /* line descriptors */
107 TMXR pas_desc = { 8, 0, 0, pas_ldsc };                  /* mux descriptor */
108 #define PAS_ENAB        pas_desc.lines
109 
110 uint32 pas (uint32 dev, uint32 op, uint32 dat);
111 void pas_ini (t_bool dtpl);
112 t_stat pasi_svc (UNIT *uptr);
113 t_stat paso_svc (UNIT *uptr);
114 t_stat pas_reset (DEVICE *dptr);
115 t_stat pas_attach (UNIT *uptr, char *cptr);
116 t_stat pas_detach (UNIT *uptr);
117 int32 pas_par (int32 cmd, int32 c);
118 t_stat pas_vlines (UNIT *uptr, int32 val, char *cptr, void *desc);
119 void pas_reset_ln (int32 i);
120 
121 /* PAS data structures
122 
123    pas_dev      PAS device descriptor
124    pas_unit     PAS unit descriptor
125    pas_reg      PAS register list
126    pas_mod      PAS modifiers list
127 */
128 
129 DIB pas_dib = { d_PAS, -1, v_PAS, pas_tplte, &pas, &pas_ini };
130 
131 UNIT pas_unit = { UDATA (&pasi_svc, UNIT_ATTABLE|UNIT_IDLE, 0), 0 };
132 
133 REG pas_reg[] = {
134     { BRDATA (STA, pas_sta, 16, 8, PAS_LINES) },
135     { BRDATA (CMD, pas_cmd, 16, 16, PAS_LINES) },
136     { BRDATA (RBUF, pas_rbuf, 16, 8, PAS_LINES) },
137     { BRDATA (XBUF, pas_xbuf, 16, 8, PAS_LINES) },
138     { BRDATA (IREQ, &int_req[l_PAS], 16, 32, PAS_LINES / 16) },
139     { BRDATA (IENB, &int_enb[l_PAS], 16, 32, PAS_LINES / 16) },
140     { BRDATA (RARM, pas_rarm, 16, 1, PAS_LINES) },
141     { BRDATA (XARM, pas_xarm, 16, 1, PAS_LINES) },
142     { BRDATA (RCHP, pas_rchp, 16, 1, PAS_LINES) },
143     { HRDATA (DEVNO, pas_dib.dno, 8), REG_HRO },
144     { NULL }
145     };
146 
147 MTAB pas_mod[] = {
148     { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT",
149       &tmxr_dscln, NULL, (void *) &pas_desc },
150     { UNIT_ATT, UNIT_ATT, "summary", NULL,
151       NULL, &tmxr_show_summ, (void *) &pas_desc },
152     { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL,
153       NULL, &tmxr_show_cstat, (void *) &pas_desc },
154     { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL,
155       NULL, &tmxr_show_cstat, (void *) &pas_desc },
156     { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
157       &set_dev, &show_dev, NULL },
158     { MTAB_XTD | MTAB_VDV, 0, "LINES", "LINES",
159       &pas_vlines, &tmxr_show_lines, (void *) &pas_desc },
160     { 0 }
161     };
162 
163 DEVICE pas_dev = {
164     "PAS", &pas_unit, pas_reg, pas_mod,
165     1, 10, 31, 1, 16, 8,
166     &tmxr_ex, &tmxr_dep, &pas_reset,
167     NULL, &pas_attach, &pas_detach,
168     &pas_dib, DEV_NET | DEV_DISABLE
169     };
170 
171 /* PASL data structures
172 
173    pasl_dev     PASL device descriptor
174    pasl_unit    PASL unit descriptor
175    pasl_reg     PASL register list
176    pasl_mod     PASL modifiers list
177 */
178 
179 UNIT pasl_unit[] = {
180     { UDATA (&paso_svc, 0, 0), PASL_WAIT },             /* all but 8 dis */
181     { UDATA (&paso_svc, 0, 0), PASL_WAIT },
182     { UDATA (&paso_svc, 0, 0), PASL_WAIT },
183     { UDATA (&paso_svc, 0, 0), PASL_WAIT },
184     { UDATA (&paso_svc, 0, 0), PASL_WAIT },
185     { UDATA (&paso_svc, 0, 0), PASL_WAIT },
186     { UDATA (&paso_svc, 0, 0), PASL_WAIT },
187     { UDATA (&paso_svc, 0, 0), PASL_WAIT },
188     { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT },
189     { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT },
190     { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT },
191     { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT },
192     { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT },
193     { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT },
194     { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT },
195     { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT },
196     { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT },
197     { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT },
198     { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT },
199     { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT },
200     { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT },
201     { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT },
202     { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT },
203     { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT },
204     { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT },
205     { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT },
206     { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT },
207     { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT },
208     { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT },
209     { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT },
210     { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT },
211     { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }
212     };
213 
214 MTAB pasl_mod[] = {
215     { TT_MODE, TT_MODE_UC, "UC", "UC", NULL },
216     { TT_MODE, TT_MODE_7B, "7b", "7B", NULL },
217     { TT_MODE, TT_MODE_8B, "8b", "8B", NULL },
218     { TT_MODE, TT_MODE_7P, "7p", "7P", NULL },
219     { UNIT_MDM, 0, "no dataset", "NODATASET", NULL },
220     { UNIT_MDM, UNIT_MDM, "dataset", "DATASET", NULL },
221     { MTAB_XTD|MTAB_VDV, 0, NULL, "DISCONNECT",
222       &tmxr_dscln, NULL, &pas_desc },
223     { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG",
224       &tmxr_set_log, &tmxr_show_log, &pas_desc },
225     { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG",
226       &tmxr_set_nolog, NULL, &pas_desc },
227     { 0 }
228     };
229 
230 REG pasl_reg[] = {
231     { URDATA (TIME, pasl_unit[0].wait, 16, 24, 0,
232               PAS_LINES, REG_NZ + PV_LEFT) },
233     { NULL }
234     };
235 
236 DEVICE pasl_dev = {
237     "PASL", pasl_unit, pasl_reg, pasl_mod,
238     PAS_LINES, 10, 31, 1, 16, 8,
239     NULL, NULL, &pas_reset,
240     NULL, NULL, NULL,
241     NULL, 0
242     };
243 
244 /* PAS: IO routine */
245 
pas(uint32 dev,uint32 op,uint32 dat)246 uint32 pas (uint32 dev, uint32 op, uint32 dat)
247 {
248 int32 ln = (dev - pas_dib.dno) >> 1;
249 int32 xmt = (dev - pas_dib.dno) & 1;
250 int32 t, old_cmd;
251 
252 switch (op) {                                           /* case IO op */
253 
254     case IO_ADR:                                        /* select */
255         return BY;                                      /* byte only */
256 
257     case IO_RD:                                         /* read */
258         pas_rchp[ln] = 0;                               /* clr chr pend */
259         pas_sta[ln] = pas_sta[ln] & ~STA_OVR;           /* clr overrun */
260         return pas_rbuf[ln];                            /* return buf */
261 
262     case IO_WD:                                         /* write */
263         pas_xbuf[ln] = dat & 0xFF;                      /* store char */
264         pas_sta[ln] = pas_sta[ln] | STA_BSY;            /* set busy */
265         sim_activate (&pasl_unit[ln], pasl_unit[ln].wait);
266         break;
267 
268     case IO_SS:                                         /* status */
269         if (xmt) {                                      /* xmt side? */
270             if (pas_ldsc[ln].conn == 0)                 /* not conn? */
271                 t = STA_NCL2S | STA_BSY;                /* busy, not clr */
272             else t = pas_sta[ln] & STA_XMT;             /* else just busy */
273             }
274         else {
275             t = pas_sta[ln] & STA_RCV;                  /* get static */
276             if (!pas_rchp[ln])                          /* no char? busy */
277                 t = t | STA_BSY;
278             if (pas_ldsc[ln].conn == 0)                 /* not connected? */
279                 t = t | STA_BSY | STA_EX;               /* = !dsr */
280             if (t & SET_EX)                             /* test for ex */
281                 t = t | STA_EX;
282             }
283         return t;
284 
285     case IO_OC:                                         /* command */
286         old_cmd = pas_cmd[ln];                          /* old cmd */
287         if (dat & CMD_TYP) {                            /* type 1? */
288             pas_cmd[ln] = (pas_cmd[ln] & 0xFF) | (dat << 8);
289             if (pas_cmd[ln] & CMD_WRT)                  /* write? */
290                 pas_xarm[ln] = int_chg (v_PASX + ln + ln, dat, pas_xarm[ln]);
291             else pas_rarm[ln] = int_chg (v_PAS + ln + ln, dat, pas_rarm[ln]);
292             }
293         else pas_cmd[ln] = (pas_cmd[ln] & ~0xFF) | dat;
294         if (pasl_unit[ln].flags & UNIT_MDM) {           /* modem ctrl? */
295             if ((pas_cmd[ln] & CMD_DTR) && (pas_sta[ln] & STA_RING))
296                 pas_sta[ln] = pas_sta[ln] & ~(STA_CROF | STA_RING);
297             if (old_cmd & ~pas_cmd[ln] & CMD_DTR) {
298                 tmxr_linemsg (&pas_ldsc[ln], "\r\nLine hangup\r\n");
299                 tmxr_reset_ln (&pas_ldsc[ln]);          /* reset line */
300                 pas_sta[ln] = pas_sta[ln] | STA_CROF;   /* no carrier */
301                 if (pas_rarm[ln])
302                     SET_INT (v_PAS + ln + ln);
303                 }
304             }
305         break;
306         }
307 
308 return 0;
309 }
310 
311 /* Unit service - receive side
312 
313    Poll all active lines for input
314    Poll for new connections
315 */
316 
pasi_svc(UNIT * uptr)317 t_stat pasi_svc (UNIT *uptr)
318 {
319 int32 ln, c, out;
320 
321 if ((uptr->flags & UNIT_ATT) == 0)                      /* attached? */
322     return SCPE_OK;
323 sim_activate (uptr, lfc_cosched (lfc_poll));            /* continue poll */
324 ln = tmxr_poll_conn (&pas_desc);                        /* look for connect */
325 if (ln >= 0) {                                          /* got one? */
326     if ((pasl_unit[ln].flags & UNIT_MDM) &&             /* modem control */
327         ((pas_cmd[ln] & CMD_DTR) == 0))                 /* & !dtr? */
328         pas_sta[ln] = pas_sta[ln] | STA_RING | STA_CROF; /* set ring, no cd */
329     else pas_sta[ln] = pas_sta[ln] & ~STA_CROF;         /* just answer */
330     if (pas_rarm[ln])                                   /* interrupt */
331         SET_INT (v_PAS + ln + ln);
332     pas_ldsc[ln].rcve = 1;                              /* rcv enabled */
333     }
334 tmxr_poll_rx (&pas_desc);                               /* poll for input */
335 for (ln = 0; ln < PAS_ENAB; ln++) {                     /* loop thru lines */
336     if (pas_ldsc[ln].conn) {                            /* connected? */
337         if ((c = tmxr_getc_ln (&pas_ldsc[ln]))) {       /* any char? */
338             pas_sta[ln] = pas_sta[ln] & ~(STA_FR | STA_PF);
339             if (pas_rchp[ln])
340                 pas_sta[ln] = pas_sta[ln] | STA_OVR;
341             if (pas_rarm[ln])
342                 SET_INT (v_PAS + ln + ln);
343             if (c & SCPE_BREAK) {                       /* break? */
344                 pas_sta[ln] = pas_sta[ln] | STA_FR;     /* framing error */
345                 pas_rbuf[ln] = 0;                       /* no character */
346                 }
347             else {                                      /* normal */
348                 out = c & 0x7F;                         /* echo is 7b */
349                 c = sim_tt_inpcvt (c, TT_GET_MODE (pasl_unit[ln].flags));
350                 if (TT_GET_MODE (pasl_unit[ln].flags) != TTUF_MODE_8B)
351                     c = pas_par (pas_cmd[ln], c);       /* apply parity */
352                 pas_rbuf[ln] = c;                       /* save char */
353                 pas_rchp[ln] = 1;                       /* char pending */
354                 if ((pas_cmd[ln] & CMD_ECHO) && pas_ldsc[ln].xmte) {
355                     TMLN *lp = &pas_ldsc[ln];           /* get line */
356                     out = sim_tt_outcvt (out, TT_GET_MODE (pasl_unit[ln].flags));
357                     if (out >= 0)                       /* output char */
358                         tmxr_putc_ln (lp, out);
359                     tmxr_poll_tx (&pas_desc);           /* poll xmt */
360                     }
361                 }                                       /* end else normal */
362             }                                           /* end if char */
363         }                                               /* end if conn */
364     else if ((pas_sta[ln] & STA_CROF) == 0) {           /* not conn, was conn? */
365         pas_sta[ln] = pas_sta[ln] | STA_CROF;           /* no carrier */
366         if (pas_rarm[ln])                               /* intr */
367             SET_INT (v_PAS + ln + ln);
368         }
369     }                                                   /* end for */
370 return SCPE_OK;
371 }
372 
373 /* Unit service - transmit side */
374 
paso_svc(UNIT * uptr)375 t_stat paso_svc (UNIT *uptr)
376 {
377 int32 c;
378 uint32 ln = uptr - pasl_unit;                           /* line # */
379 
380 if (pas_ldsc[ln].conn) {                                /* connected? */
381     if (pas_ldsc[ln].xmte) {                            /* xmt enabled? */
382         TMLN *lp = &pas_ldsc[ln];                       /* get line */
383         if (TT_GET_MODE (pasl_unit[ln].flags) == TTUF_MODE_8B)
384             c = pas_par (pas_cmd[ln], pas_xbuf[ln]);    /* apply parity */
385         else c = sim_tt_outcvt (pas_xbuf[ln], TT_GET_MODE (pasl_unit[ln].flags));
386         if (c >= 0) {
387             tmxr_putc_ln (lp, c);                       /* output char */
388             }
389         tmxr_poll_tx (&pas_desc);                       /* poll xmt */
390         }
391     else {                                              /* buf full */
392         tmxr_poll_tx (&pas_desc);                       /* poll xmt */
393         sim_activate (uptr, pasl_unit[ln].wait);        /* wait */
394         return SCPE_OK;
395         }
396     }
397 pas_sta[ln] = pas_sta[ln] & ~STA_BSY;                   /* not busy */
398 if (pas_xarm[ln])                                       /* set intr */
399     SET_INT (v_PASX + ln + ln);
400 return SCPE_OK;
401 }
402 
pas_par(int32 cmd,int32 c)403 int32 pas_par (int32 cmd, int32 c)
404 {
405 int32 pf = GET_PAR (cmd);
406 static const uint8 odd_par[] = {
407     0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0,                 /* 00 */
408     0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80,
409     0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80,                 /* 10 */
410     0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0,
411     0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80,                 /* 20 */
412     0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0,
413     0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0,                 /* 30 */
414     0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80,
415     0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80,                 /* 40 */
416     0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0,
417     0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0,                 /* 50 */
418     0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80,
419     0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0,                 /* 60 */
420     0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80,
421     0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80,                 /* 70 */
422     0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0,
423     0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80,                 /* 80 */
424     0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0,
425     0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0,                 /* 90 */
426     0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80,
427     0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0,                 /* A0 */
428     0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80,
429     0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80,                 /* B0 */
430     0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0,
431     0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0,                 /* C0 */
432     0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80,
433     0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80,                 /* D0 */
434     0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0,
435     0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80,                 /* E0 */
436     0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0,
437     0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0,                 /* F0 */
438     0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80
439     };
440 
441 switch (pf) {                                           /* case on parity */
442 
443     case PAR_ODD:
444         return (odd_par[c & 0x7F]) | (c & 0x7F);
445 
446     case PAR_EVEN:
447         return (odd_par[c & 0x7F] ^ 0x80) | (c & 0x7F);
448 
449     case PAR_NONE:
450     case PAR_RAW:
451         break;
452         }
453 
454 return c & 0xFF;
455 }
456 
457 /* Reset routine */
458 
pas_reset(DEVICE * dptr)459 t_stat pas_reset (DEVICE *dptr)
460 {
461 int32 i;
462 
463 if (dptr->flags & DEV_DIS) {                            /* disabled? */
464     pas_dev.flags = pas_dev.flags | DEV_DIS;            /* disable lines */
465     pasl_dev.flags = pasl_dev.flags | DEV_DIS;
466     }
467 else {
468     pas_dev.flags = pas_dev.flags & ~DEV_DIS;           /* enable lines */
469     pasl_dev.flags = pasl_dev.flags & ~DEV_DIS;
470     }
471 if (pas_unit.flags & UNIT_ATT)                          /* master att? */
472     sim_activate (&pas_unit, lfc_poll);
473 else sim_cancel (&pas_unit);                            /* else stop */
474 for (i = 0; i < PAS_LINES; i++)
475     pas_reset_ln (i);
476 return SCPE_OK;
477 }
478 
479 /* Attach master unit */
480 
pas_attach(UNIT * uptr,char * cptr)481 t_stat pas_attach (UNIT *uptr, char *cptr)
482 {
483 t_stat r;
484 
485 r = tmxr_attach (&pas_desc, uptr, cptr);                /* attach */
486 if (r != SCPE_OK)                                       /* error */
487     return r;
488 sim_activate (uptr, 100);                               /* quick poll */
489 return SCPE_OK;
490 }
491 
492 /* Detach master unit */
493 
pas_detach(UNIT * uptr)494 t_stat pas_detach (UNIT *uptr)
495 {
496 int32 i;
497 t_stat r;
498 
499 r = tmxr_detach (&pas_desc, uptr);                      /* detach */
500 for (i = 0; i < PAS_LINES; i++)                         /* disable rcv */
501     pas_ldsc[i].rcve = 0;
502 sim_cancel (uptr);                                      /* stop poll */
503 return r;
504 }
505 
506 /* Change number of lines */
507 
pas_vlines(UNIT * uptr,int32 val,char * cptr,void * desc)508 t_stat pas_vlines (UNIT *uptr, int32 val, char *cptr, void *desc)
509 {
510 int32 newln, i, t;
511 t_stat r;
512 
513 if (cptr == NULL)
514     return SCPE_ARG;
515 newln = get_uint (cptr, 10, PAS_LINES, &r);
516 if ((r != SCPE_OK) || (newln == PAS_ENAB))
517     return r;
518 if (newln == 0)
519     return SCPE_ARG;
520 if (newln < PAS_ENAB) {
521     for (i = newln, t = 0; i < PAS_ENAB; i++)
522         t = t | pas_ldsc[i].conn;
523     if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE))
524         return SCPE_OK;
525     for (i = newln; i < PAS_ENAB; i++) {
526         if (pas_ldsc[i].conn) {
527             tmxr_linemsg (&pas_ldsc[i], "\r\nOperator disconnected line\r\n");
528             tmxr_reset_ln (&pas_ldsc[i]);               /* reset line */
529 			}
530         pasl_unit[i].flags = pasl_unit[i].flags | UNIT_DIS;
531         pas_reset_ln (i);
532         }
533     }
534 else {
535     for (i = PAS_ENAB; i < newln; i++) {
536         pasl_unit[i].flags = pasl_unit[i].flags & ~UNIT_DIS;
537         pas_reset_ln (i);
538         }
539     }
540 PAS_ENAB = newln;
541 return SCPE_OK;
542 }
543 
544 /* Reset an individual line */
545 
pas_reset_ln(int32 i)546 void pas_reset_ln (int32 i)
547 {
548 CLR_INT (v_PAS + i + i);                                /* clear int */
549 CLR_ENB (v_PAS + i + i);
550 CLR_INT (v_PASX + i + i);                               /* disable int */
551 CLR_ENB (v_PASX + i + i);
552 pas_rarm[i] = pas_xarm[i] = 0;                          /* disarm int */
553 pas_rbuf[i] = pas_xbuf[i] = 0;                          /* clear state */
554 pas_cmd[i] = 0;
555 pas_rchp[i] = 0;
556 pas_sta[i] = 0;
557 if (pas_ldsc[i].conn == 0)                              /* clear carrier */
558     pas_sta[i] = pas_sta[i] | STA_CROF;
559 sim_cancel (&pasl_unit[i]);
560 return;
561 }
562 
563 /* Init template */
564 
pas_ini(t_bool dtpl)565 void pas_ini (t_bool dtpl)
566 {
567 int32 i, j;
568 
569 for (i = j = 0; i < PAS_ENAB; i++) {
570     pas_tplte[j] = j;
571     pas_tplte[j + 1] = j + o_PASX;
572     j = j + 2;
573     }
574 pas_tplte[j] = TPL_END;
575 return;
576 }
577