1 /* i7094_com.c: IBM 7094 7750 communications interface simulator
2 
3    Copyright (c) 2005-2010, 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    com          7750 controller
27    coml         7750 lines
28 
29    12-Aug-10    RMS     Major rewrite for CTSS (Dave Pitts)
30    19-Nov-08    RMS     Revised for common TMXR show routines
31 
32    This module implements an abstract simulator for the IBM 7750 communications
33    computer as used by the CTSS system.  The 7750 supports up to 112 lines;
34    the simulator supports 33.  The 7750 can handle both high-speed lines, in
35    6b and 12b mode, and normal terminals, in 12b mode only; the simulator
36    supports only terminals.  The 7750 can handle many different kinds of
37    terminals; the simulator supports only a limited subset.
38 
39    Input is asynchronous and line buffered.  When valid input (a line or a
40    control message) is available, the 7750 sets ATN1 to signal availability of
41    input.  When the 7094 issues a CTLRN, the 7750 gathers available input characters
42    into a message.  The message has a 12b sequence number, followed by 12b line
43    number/character pairs, followed by end-of-medium (03777).  Input characters
44    can either be control characters (bit 02000 set) or data characters.  Data
45    characters are 1's complemented and are 8b wide: 7 data bits and 1 parity
46    bit (which may be 0).
47 
48    Output is synchronous.  When the 7094 issues a CTLWN, the 7750 interprets
49    the channel output as a message.  The message has a 12b line number, followed
50    by a 12b character count, followed by characters, followed by end-of-medium.
51    If bit 02000 of the line number is set, the characters are 12b wide.  If
52    bit 01000 is set, the message is a control message.  12b characters consist
53    of 7 data bits, 1 parity bit, and 1 start bit.  Data characters are 1's
54    complemented.  Data character 03777 is special and causes the 7750 to
55    repeat the previous bit for the number of bit times specified in the next
56    character.  This is used to generate delays for positioning characters.
57 
58    The 7750 supports flow control for output.  To help the 7094 account for
59    usage of 7750 buffer memory, the 7750 sends 'character output completion'
60    messages for every 'n' characters output on a line, where n <= 31.
61 
62    Note that the simulator console is mapped in as line n+1.
63 */
64 
65 #include "i7094_defs.h"
66 #include "sim_sock.h"
67 #include "sim_tmxr.h"
68 #include <ctype.h>
69 
70 #define COM_MLINES      31                              /* mux lines */
71 #define COM_TLINES      (COM_MLINES + 1)                /* total lines */
72 #define COM_BUFSIZ      120                             /* max chan transfer */
73 #define COM_PKTSIZ      16384                           /* character buffer */
74 
75 #define UNIT_V_2741     (TTUF_V_UF + 0)                 /* 2741 - ni */
76 #define UNIT_V_K35      (TTUF_V_UF + 1)                 /* KSR-35 */
77 #define UNIT_2741       (1 << UNIT_V_2741)
78 #define UNIT_K35        (1 << UNIT_V_K35)
79 
80 #define CONN            u3                              /* line is connected */
81 #define NEEDID          u4                              /* need to send ID */
82 #define NOECHO          u5                              /* no echo */
83 #define INPP            u6                              /* input pending */
84 
85 #define COM_INIT_POLL   8000                            /* polling interval */
86 #define COMC_WAIT       2                               /* channel delay time */
87 #define COML_WAIT       1000                            /* char delay time */
88 #define COM_LBASE       4                               /* start of lines */
89 
90 /* Input threads */
91 
92 #define COM_PLU         0                               /* multiplexor poll */
93 #define COM_CIU         1                               /* console input */
94 #define COM_CHU         2                               /* channel transfer */
95 #define COM_SNS         3                               /* sense transfer */
96 
97 /* Communications input */
98 
99 #define COMI_VALIDL     02000                           /* valid line flag */
100 #define COMI_PARITY     00200                           /* parity bit */
101 #define COMI_DIALUP     02001                           /* dialup */
102 #define COMI_ENDID      02002                           /* end ID */
103 #define COMI_INTR       02003                           /* interrupt */
104 #define COMI_QUIT       02004                           /* quit */
105 #define COMI_HANGUP     02005                           /* hangup */
106 #define COMI_EOM        03777                           /* end of medium */
107 #define COMI_COMP(x)    ((uint16) (03000 + ((x) & COMI_CMAX)))
108 #define COMI_K35        1                               /* KSR-35 ID */
109 #define COMI_K37        7                               /* KSR-37 ID */
110 #define COMI_2741       8                               /* 2741 ID */
111 #define COMI_CMAX       31                              /* max chars returned */
112 #define COMI_BMAX       50                              /* buffer max, words */
113 #define COMI_12BMAX     ((3 * COMI_BMAX) - 1)           /* last 12b char */
114 
115 /* Communications output */
116 
117 #define COMO_LIN12B     0200000000000                   /* line is 12b */
118 #define COMO_LINCTL     0100000000000                   /* control msg */
119 #define COMO_GETLN(x)   (((uint32) ((x) >> 24)) & 0777)
120 #define COMO_CTLRST     00000                           /* control reset */
121 #define COMO_BITRPT     03777                           /* bit repeat */
122 #define COMO_EOM12B     07777                           /* end of medium */
123 #define COMO_BMAX       94                              /* buffer max, words */
124 #define COMO_12BMAX     ((3 * COMO_BMAX) - 1)
125 
126 /* Status word (60b) */
127 
128 #define COMS_PCHK       004000000000000000000           /* prog check */
129 #define COMS_DCHK       002000000000000000000           /* data check */
130 #define COMS_EXCC       001000000000000000000           /* exc cond */
131 #define COMS_MLNT       000040000000000000000           /* message length check */
132 #define COMS_CHNH       000020000000000000000           /* channel hold */
133 #define COMS_CHNQ       000010000000000000000           /* channel queue full */
134 #define COMS_ITMO       000000100000000000000           /* interface timeout */
135 #define COMS_DATR       000000004000000000000           /* data message ready */
136 #define COMS_INBF       000000002000000000000           /* input buffer free */
137 #define COMS_SVCR       000000001000000000000           /* service message ready */
138 #define COMS_PALL       000000000000000000000
139 #define COMS_DALL       000000000000000000000
140 #define COMS_EALL       000000000000000000000
141 #define COMS_DYN        000000007000000000000
142 
143 /* Report variables */
144 
145 #define COMR_FQ         1                               /* free queue */
146 #define COMR_IQ         2                               /* input queue */
147 #define COMR_OQ         4                               /* output queue */
148 
149 /* List heads and entries */
150 
151 typedef struct {
152     uint16              head;
153     uint16              tail;
154     } LISTHD;
155 
156 typedef struct {
157     uint16              next;
158     uint16              data;
159     } LISTENT;
160 
161 /* The 7750 character buffer is maintained as linked lists.  The lists are:
162 
163    free                 free list
164    inpq[ln]             input queue for line n
165    outq[ln]             output queue for line ln
166 
167    Links are done as subscripts in array com_pkt.  This allows the list
168    headers and the queues themselves to be saved and restored. */
169 
170 uint32 com_ch = CH_E;                                   /* saved channel */
171 uint32 com_enab = 0;                                    /* 7750 enabled */
172 uint32 com_msgn = 0;                                    /* next input msg num */
173 uint32 com_sta = 0;                                     /* 7750 state */
174 uint32 com_stop = 0;                                    /* channel stop */
175 uint32 com_quit = 003;                                  /* quit code */
176 uint32 com_intr = 0;                                    /* interrupt code */
177 uint32 com_bptr = 0;                                    /* buffer pointer */
178 uint32 com_blim = 0;                                    /* buffer count */
179 uint32 com_tps = 50;                                    /* polls/second */
180 t_uint64 com_sns = 0;                                   /* sense word */
181 t_uint64 com_chob = 0;                                  /* chan output buf */
182 uint32 com_chob_v = 0;                                  /* valid flag */
183 t_uint64 com_buf[COM_BUFSIZ];                           /* channel buffer */
184 LISTHD com_free;                                        /* free list */
185 uint32 com_not_ret[COM_TLINES] = { 0 };                 /* chars not returned */
186 LISTHD com_inpq[COM_TLINES] = { 0 };                    /* input queues */
187 LISTHD com_outq[COM_TLINES] = { 0 };                    /* output queues */
188 LISTENT com_pkt[COM_PKTSIZ];                            /* character packets */
189 TMLN com_ldsc[COM_MLINES] = { 0 };                      /* line descriptors */
190 TMXR com_desc = { COM_MLINES, 0, 0, com_ldsc };         /* mux descriptor */
191 
192 /* Even parity truth table */
193 
194 static const uint8 com_epar[128] = {
195     0, 1, 1, 0, 1, 0, 0, 1,
196     1, 0, 0, 1, 0, 1, 1, 0,
197     1, 0, 0, 1, 0, 1, 1, 0,
198     0, 1, 1, 0, 1, 0, 0, 1,
199     1, 0, 0, 1, 0, 1, 1, 0,
200     0, 1, 1, 0, 1, 0, 0, 1,
201     0, 1, 1, 0, 1, 0, 0, 1,
202     1, 0, 0, 1, 0, 1, 1, 0,
203     1, 0, 0, 1, 0, 1, 1, 0,
204     0, 1, 1, 0, 1, 0, 0, 1,
205     0, 1, 1, 0, 1, 0, 0, 1,
206     1, 0, 0, 1, 0, 1, 1, 0,
207     0, 1, 1, 0, 1, 0, 0, 1,
208     1, 0, 0, 1, 0, 1, 1, 0,
209     1, 0, 0, 1, 0, 1, 1, 0,
210     0, 1, 1, 0, 1, 0, 0, 1
211     };
212 
213 extern uint32 ch_req;
214 
215 t_stat com_chsel (uint32 ch, uint32 sel, uint32 unit);
216 t_stat com_chwr (uint32 ch, t_uint64 val, uint32 flags);
217 t_stat comi_svc (UNIT *uptr);
218 t_stat comc_svc (UNIT *uptr);
219 t_stat como_svc (UNIT *uptr);
220 t_stat coms_svc (UNIT *uptr);
221 t_stat comti_svc (UNIT *uptr);
222 t_stat comto_svc (UNIT *uptr);
223 t_stat com_reset (DEVICE *dptr);
224 t_stat com_attach (UNIT *uptr, char *cptr);
225 t_stat com_detach (UNIT *uptr);
226 t_stat com_show_ctrl (FILE *st, UNIT *uptr, int32 val, void *desc);
227 t_stat com_show_freeq (FILE *st, UNIT *uptr, int32 val, void *desc);
228 t_stat com_show_allq (FILE *st, UNIT *uptr, int32 val, void *desc);
229 t_stat com_show_oneq (FILE *st, UNIT *uptr, int32 val, void *desc);
230 void com_reset_ln (uint32 i);
231 uint16 com_get_nexti (uint32 *ln);
232 uint16 com_gethd_free (LISTHD *lh);
233 uint16 com_gethd (LISTHD *lh);
234 uint16 com_gettl_free (LISTHD *lh);
235 uint16 com_gettl (LISTHD *lh);
236 t_bool com_new_puttl (LISTHD *lh, uint16 val);
237 void com_puttl (LISTHD *lh, uint16 ent);
238 t_bool com_test_inp (void);
239 void com_set_inpp (uint32 ln);
240 t_uint64 com_getob (uint32 ch);
241 t_bool com_qdone (uint32 ch);
242 void com_end (uint32 ch, uint32 fl, uint32 st);
243 t_stat com_send_id (uint32 ln);
244 uint32 com_gen_ccmp (uint32 ln);
245 t_bool com_queue_in (uint32 ln, uint32 ch);
246 uint32 com_queue_out (uint32 ln, uint32 *c1);
247 void com_set_sns (t_uint64 stat);
248 
249 /* COM data structures
250 
251    com_dev      COM device descriptor
252    com_unit     COM unit descriptor
253    com_reg      COM register list
254    com_mod      COM modifiers list
255 */
256 
257 DIB com_dib = { &com_chsel, &com_chwr };
258 
259 UNIT com_unit[] = {
260     { UDATA (&comi_svc, UNIT_ATTABLE, 0), COM_INIT_POLL },
261     { UDATA (&comti_svc, UNIT_DIS, 0), KBD_POLL_WAIT },
262     { UDATA (&comc_svc, UNIT_DIS, 0), COMC_WAIT },
263     { UDATA (&coms_svc, UNIT_DIS, 0), COMC_WAIT }
264     };
265 
266 UNIT coml_unit[] = {
267     { UDATA (&como_svc, 0, 0), COML_WAIT },
268     { UDATA (&como_svc, 0, 0), COML_WAIT },
269     { UDATA (&como_svc, 0, 0), COML_WAIT },
270     { UDATA (&como_svc, 0, 0), COML_WAIT },
271     { UDATA (&como_svc, 0, 0), COML_WAIT },
272     { UDATA (&como_svc, 0, 0), COML_WAIT },
273     { UDATA (&como_svc, 0, 0), COML_WAIT },
274     { UDATA (&como_svc, 0, 0), COML_WAIT },
275     { UDATA (&como_svc, 0, 0), COML_WAIT },
276     { UDATA (&como_svc, 0, 0), COML_WAIT },
277     { UDATA (&como_svc, 0, 0), COML_WAIT },
278     { UDATA (&como_svc, 0, 0), COML_WAIT },
279     { UDATA (&como_svc, 0, 0), COML_WAIT },
280     { UDATA (&como_svc, 0, 0), COML_WAIT },
281     { UDATA (&como_svc, 0, 0), COML_WAIT },
282     { UDATA (&como_svc, 0, 0), COML_WAIT },
283     { UDATA (&como_svc, 0, 0), COML_WAIT },
284     { UDATA (&como_svc, 0, 0), COML_WAIT },
285     { UDATA (&como_svc, 0, 0), COML_WAIT },
286     { UDATA (&como_svc, 0, 0), COML_WAIT },
287     { UDATA (&como_svc, 0, 0), COML_WAIT },
288     { UDATA (&como_svc, 0, 0), COML_WAIT },
289     { UDATA (&como_svc, 0, 0), COML_WAIT },
290     { UDATA (&como_svc, 0, 0), COML_WAIT },
291     { UDATA (&como_svc, 0, 0), COML_WAIT },
292     { UDATA (&como_svc, 0, 0), COML_WAIT },
293     { UDATA (&como_svc, 0, 0), COML_WAIT },
294     { UDATA (&como_svc, 0, 0), COML_WAIT },
295     { UDATA (&como_svc, 0, 0), COML_WAIT },
296     { UDATA (&como_svc, 0, 0), COML_WAIT },
297     { UDATA (&como_svc, 0, 0), COML_WAIT },
298     { UDATA (&comto_svc, 0, 0), COML_WAIT },
299     };
300 
301 REG com_reg[] = {
302     { FLDATA (ENABLE, com_enab, 0) },
303     { ORDATA (STATE, com_sta, 6) },
304     { ORDATA (MSGNUM, com_msgn, 12) },
305     { ORDATA (SNS, com_sns, 60) },
306     { ORDATA (CHOB, com_chob, 36) },
307     { FLDATA (CHOBV, com_chob_v, 0) },
308     { FLDATA (STOP, com_stop, 0) },
309     { ORDATA (QUIT, com_quit, 7) },
310     { ORDATA (INTR, com_intr, 7) },
311     { BRDATA (BUF, com_buf, 8, 36, COM_BUFSIZ) },
312     { DRDATA (BPTR, com_bptr, 7), REG_RO },
313     { DRDATA (BLIM, com_blim, 7), REG_RO },
314     { BRDATA (NRET, com_not_ret, 10, 32, COM_TLINES), REG_RO + PV_LEFT },
315     { URDATA (NEEDID, coml_unit[0].NEEDID, 8, 1, 0, COM_TLINES, 0) },
316     { URDATA (NOECHO, coml_unit[0].NOECHO, 8, 1, 0, COM_TLINES, 0) },
317     { URDATA (INPP, coml_unit[0].INPP, 8, 1, 0, COM_TLINES, 0) },
318     { BRDATA (FREEQ, &com_free, 10, 16, 2) },
319     { BRDATA (INPQ, com_inpq, 10, 16, 2 * COM_TLINES) },
320     { BRDATA (OUTQ, com_outq, 10, 16, 2 * COM_TLINES) },
321     { BRDATA (PKTB, com_pkt, 10, 16, 2 * COM_PKTSIZ) },
322     { DRDATA (TTIME, com_unit[COM_CIU].wait, 24), REG_NZ + PV_LEFT },
323     { DRDATA (WTIME, com_unit[COM_CHU].wait, 24), REG_NZ + PV_LEFT },
324     { DRDATA (CHAN, com_ch, 3), REG_HRO },
325     { NULL }
326     };
327 
328 MTAB com_mod[] = {
329     { UNIT_ATT, UNIT_ATT, "summary", NULL,
330       NULL, &tmxr_show_summ, (void *) &com_desc },
331     { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL,
332       NULL, &tmxr_show_cstat, (void *) &com_desc },
333     { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL,
334       NULL, &tmxr_show_cstat, (void *) &com_desc },
335     { MTAB_XTD | MTAB_VDV | MTAB_NMO, COMR_FQ, "FREEQ", NULL,
336       NULL, &com_show_ctrl, 0 },
337     { MTAB_XTD | MTAB_VDV | MTAB_NMO, COMR_IQ, "INPQ", NULL,
338       NULL, &com_show_ctrl, 0 },
339     { MTAB_XTD | MTAB_VDV | MTAB_NMO, COMR_OQ, "OUTQ", NULL,
340       NULL, &com_show_ctrl, 0 },
341     { MTAB_XTD | MTAB_VDV | MTAB_NMO, -1, "ALL", NULL,
342       NULL, &com_show_ctrl, 0 },
343     { 0 }
344     };
345 
346 DEVICE com_dev = {
347     "COM", com_unit, com_reg, com_mod,
348     3, 10, 31, 1, 16, 8,
349     &tmxr_ex, &tmxr_dep, &com_reset,
350     NULL, &com_attach, &com_detach,
351     &com_dib, DEV_NET | DEV_DIS
352     };
353 
354 /* COML data structures
355 
356    coml_dev     COML device descriptor
357    coml_unit    COML unit descriptor
358    coml_reg     COML register list
359    coml_mod     COML modifiers list
360 */
361 
362 MTAB coml_mod[] = {
363     { UNIT_K35+UNIT_2741, 0        , "KSR-37", "KSR-37", NULL },
364     { UNIT_K35+UNIT_2741, UNIT_K35 , "KSR-35", "KSR-35", NULL },
365 //  { UNIT_K35+UNIT_2741, UNIT_2741, "2741",   "2741", NULL },
366     { MTAB_XTD|MTAB_VUN, 0, NULL, "DISCONNECT",
367       &tmxr_dscln, NULL, (void *) &com_desc },
368     { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG",
369       &tmxr_set_log, &tmxr_show_log, (void*) &com_desc },
370     { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG",
371       &tmxr_set_nolog, NULL, (void *) &com_desc },
372     { MTAB_XTD | MTAB_VUN | MTAB_NMO, 0, "INPQ", NULL,
373       NULL, &com_show_oneq, 0 },
374     { MTAB_XTD | MTAB_VUN | MTAB_NMO, 1, "OUTQ", NULL,
375       NULL, &com_show_oneq, 0 },
376     { 0 }
377     };
378 
379 REG coml_reg[] = {
380     { URDATA (TIME, coml_unit[0].wait, 10, 24, 0,
381               COM_TLINES, REG_NZ + PV_LEFT) },
382     { NULL }
383     };
384 
385 DEVICE coml_dev = {
386     "COML", coml_unit, coml_reg, coml_mod,
387     COM_TLINES, 10, 31, 1, 16, 8,
388     NULL, NULL, &com_reset,
389     NULL, NULL, NULL,
390     NULL, DEV_DIS
391     };
392 
393 /* COM: channel select */
394 
com_chsel(uint32 ch,uint32 sel,uint32 unit)395 t_stat com_chsel (uint32 ch, uint32 sel, uint32 unit)
396 {
397 com_ch = ch;                                            /* save channel */
398 if (sim_is_active (&com_unit[COM_CHU]) ||               /* not idle? */
399     sim_is_active (&com_unit[COM_SNS])) {
400     com_end (ch, CHINT_SEQC, 0);                        /* end, seq check */
401     return SCPE_OK;
402     }
403 
404 switch (sel) {                                          /* case on select */
405 
406     case CHSL_RDS:                                      /* read */
407     case CHSL_WRS:                                      /* write */
408         com_sns = 0;                                    /* clear status */
409         sim_activate (&com_unit[COM_CHU], com_unit[COM_CHU].wait);
410         break;
411 
412     case CHSL_SNS:                                      /* sense */
413         sim_activate (&com_unit[COM_SNS], com_unit[COM_SNS].wait);
414         break;
415 
416     case CHSL_CTL:                                      /* control */
417     default:                                            /* other */
418         return STOP_ILLIOP;
419         }
420 
421 com_stop = 0;                                           /* clear stop */
422 com_sta = sel;                                          /* set initial state */
423 return SCPE_OK;
424 }
425 
426 /* Channel write, from 7909 channel program */
427 
com_chwr(uint32 ch,t_uint64 val,uint32 stopf)428 t_stat com_chwr (uint32 ch, t_uint64 val, uint32 stopf)
429 {
430 if (stopf)
431     com_stop = 1;
432 else {
433     com_chob = val;                                     /* store data */
434     com_chob_v = 1;                                     /* set valid */
435     }
436 return SCPE_OK;
437 }
438 
439 /* Unit service - SNS */
440 
coms_svc(UNIT * uptr)441 t_stat coms_svc (UNIT *uptr)
442 {
443 t_uint64 dat;
444 
445 switch (com_sta) {                                      /* case on state */
446 
447     case CHSL_SNS:                                      /* prepare data */
448         com_sns &= ~COMS_DYN;                           /* clear dynamic flags */
449         if (com_free.head)                              /* free space? */
450             com_set_sns (COMS_INBF);
451         if (com_test_inp ())                            /* pending input? */
452             com_set_sns (COMS_DATR);
453         com_buf[0] = (com_sns >> 24) & DMASK;           /* buffer is 2 words */
454         com_buf[1] = (com_sns << 12) & DMASK;
455         com_bptr = 0;
456         com_blim = 2;
457         com_sta = CHSL_SNS|CHSL_2ND;                    /* 2nd state */
458         break;
459 
460     case CHSL_SNS|CHSL_2ND:                             /* second state */
461         if (com_bptr >= com_blim) {                     /* end of buffer? */
462             ch9_set_end (com_ch, 0);                    /* set end */
463             ch_req |= REQ_CH (com_ch);                  /* request channel */
464             com_sta = CHSL_SNS|CHSL_3RD;                /* 3rd state */
465             sim_activate (uptr, 10 * uptr->wait);       /* longer wait */
466             return SCPE_OK;
467             }
468         dat = com_buf[com_bptr++];                      /* get word */
469         if (!com_stop)                                  /* send wd to chan */
470             ch9_req_rd (com_ch, dat);
471         break;
472 
473     case CHSL_SNS|CHSL_3RD:                             /* 3rd state */
474         if (com_qdone (com_ch))                         /* done? exit */
475             return SCPE_OK;
476         com_sta = CHSL_SNS;                             /* repeat sequence */
477         break;
478         }
479 
480 sim_activate (uptr, uptr->wait);                        /* sched next */
481 return SCPE_OK;
482 }
483 
484 /* Unit service - channel program */
485 
comc_svc(UNIT * uptr)486 t_stat comc_svc (UNIT *uptr)
487 {
488 uint32 i, j, k, ccnt, ln, uln;
489 uint16 chr, ent;
490 t_uint64 dat;
491 
492 switch (com_sta) {                                      /* case on state */
493 
494     case CHSL_RDS:                                      /* read start */
495         for (i = 0; i < COM_BUFSIZ; i++)                /* clear chan buf */
496             com_buf[i] = 0;
497         com_buf[0] = com_msgn;                          /* 1st char is msg num */
498         com_msgn = (com_msgn + 1) & 03777;              /* incr msg num */
499         for (i = 1, j = 0, ln = 0;                      /* check all lines */
500             (ln < COM_TLINES) && (i < COMI_12BMAX);     /* until buffer full */
501             ln++) {
502             chr = (uint16) com_gen_ccmp (ln);           /* completion msg? */
503             if ((chr == 0) && coml_unit[ln].INPP) {     /* no, line input? */
504                 ent = com_gethd_free (&com_inpq[ln]);   /* get first char */
505                 if (ent != 0)                           /* any input? */
506                     chr = com_pkt[ent].data;            /* return char */
507                 else coml_unit[i].INPP = 0;             /* this line is idle */
508                 }                                       /* end if input pending */
509             if (chr != 0) {                             /* got something? */
510                 if ((i++ % 3) == 0)                     /* next word? */
511                     j++;
512                 com_buf[j] = (com_buf[j] << 12) |       /* pack line number */
513                     ((t_uint64) ((ln + COM_LBASE) | COMI_VALIDL));
514                 if ((i++ % 3) == 0)                     /* next word? */
515                     j++;
516                 com_buf[j] = (com_buf[j] << 12) |       /* pack data */
517                     ((t_uint64) (chr & 07777));
518                 }                                       /* end if char */
519             }                                           /* end for buffer */
520         for (k = i % 3; k < 3; k++) {                   /* fill with EOM */
521             if (k == 0)                                 /* next word? */
522                 j++;
523             com_buf[j] = (com_buf[j] << 12) | COMI_EOM;
524             }
525         com_bptr = 0;                                   /* init buf ptr */
526         com_blim = j + 1;                               /* save buf size */
527         com_sta = CHSL_RDS|CHSL_2ND;                    /* next state */
528         break;
529 
530     case CHSL_RDS|CHSL_2ND:                             /* read xmit word */
531         if (com_bptr >= com_blim)                       /* transfer done? */
532             com_end (com_ch, 0, CHSL_RDS|CHSL_3RD);     /* end, next state */
533         else {                                          /* more to do */
534             dat = com_buf[com_bptr++];                  /* get word */
535             if (!com_stop)                              /* give to channel */
536                 ch9_req_rd (com_ch, dat);
537             }
538         break;
539 
540     case CHSL_RDS|CHSL_3RD:                             /* read end */
541         if (com_qdone (com_ch)) {                       /* done? */
542             if (com_test_inp ())                        /* more data waiting? */
543                 ch9_set_atn (com_ch);
544             return SCPE_OK;                             /* exit */
545             }
546         com_sta = CHSL_RDS;                             /* repeat sequence */
547         break;
548 
549     case CHSL_WRS:                                      /* write start */
550         for (i = 0; i < COM_BUFSIZ; i++)                /* clear chan buf */
551             com_buf[i] = 0;
552         com_bptr = 0;                                   /* init buf ptr */
553         com_sta = CHSL_WRS|CHSL_2ND;                    /* next state */
554         ch_req |= REQ_CH (com_ch);                      /* request channel */
555         com_chob = 0;                                   /* clr, inval buf */
556         com_chob_v = 0;
557         break;
558 
559     case CHSL_WRS|CHSL_2ND:                             /* write first word */
560         dat = com_getob (com_ch);                       /* get word? */
561         if (dat == 0777777777777) {                     /* turn on? */
562             com_enab = 1;                               /* enable 7750 */
563             com_msgn = 0;                               /* init message # */
564             com_end (com_ch, 0, CHSL_WRS|CHSL_4TH);     /* end, last state */
565             }
566         else if (dat & COMO_LINCTL) {                   /* control message? */
567             ln = COMO_GETLN (dat);                      /* line number */
568             if (ln >= (COM_TLINES + COM_LBASE))         /* invalid line? */
569                 return STOP_INVLIN;
570             chr = (uint16) ((dat >> 12) & 07777);       /* control message */
571             if (chr != COMO_CTLRST)                     /* char must be 0 */
572                 return STOP_INVMSG;
573             if (ln >= COM_LBASE)
574                 com_reset_ln (ln - COM_LBASE);
575             com_end (com_ch, 0, CHSL_WRS|CHSL_4TH);     /* end, last state */
576             }
577         else {                                          /* data message */
578             ccnt = (((uint32) dat >> 12) & 07777) + 1;  /* char count plus EOM */
579             if (dat & COMO_LIN12B)                      /* 12b? double */
580                 ccnt = ccnt << 1;
581             com_blim = (ccnt + 6 + 5) / 6;              /* buffer limit */
582             if ((com_blim == 1) || (com_blim >= COMO_BMAX))
583                 return STOP_INVMSG;
584             com_buf[com_bptr++] = dat;                  /* store word */
585             com_sta = CHSL_WRS|CHSL_3RD;                /* next state */
586             ch_req |= REQ_CH (com_ch);                  /* request channel */
587             }
588         break;
589 
590     case CHSL_WRS|CHSL_3RD:                             /* other words */
591         dat = com_getob (com_ch);                       /* get word */
592         com_buf[com_bptr++] = dat;                      /* store word */
593         if (com_bptr >= com_blim) {                     /* transfer done? */
594             ln = COMO_GETLN (com_buf[0]);               /* line number */
595             if (ln >= (COM_TLINES + COM_LBASE))         /* invalid line? */
596                 return STOP_INVLIN;
597             if ((com_buf[0] & COMO_LIN12B) &&           /* 12b message? */
598                 (ln >= COM_LBASE)) {
599                 uln = ln - COM_LBASE;                   /* unit number */
600                 for (i = 2, j = 0; i < COMO_12BMAX; i++) { /* unpack 12b char */
601                     if ((i % 3) == 0)
602                         j++;
603                     chr = (uint16) (com_buf[j] >> ((2 - (i % 3)) * 12)) & 07777;
604                     if (chr == COMO_EOM12B)             /* EOM? */
605                         break;
606                     if (!com_new_puttl (&com_outq[uln], chr))
607                         return STOP_NOOFREE;            /* append to outq */
608                     }
609                 sim_activate (&coml_unit[uln], coml_unit[uln].wait);
610                 }
611             com_end (com_ch, 0, CHSL_WRS|CHSL_4TH);     /* end, last state */
612             }
613         else if (!com_stop)                             /* request channel */
614             ch_req |= REQ_CH (com_ch);
615         break;
616 
617     case CHSL_WRS|CHSL_4TH:                             /* buffer done */
618         if (com_qdone (com_ch))                         /* done? */
619             return SCPE_OK;                             /* exit */
620         com_sta = CHSL_WRS;                             /* repeat sequence */
621         break;
622 
623     default:
624         return SCPE_IERR;
625         }
626 
627 sim_activate (uptr, uptr->wait);
628 return SCPE_OK;
629 }
630 
631 /* Unit service - console receive - always running, even if device is not */
632 
comti_svc(UNIT * uptr)633 t_stat comti_svc (UNIT *uptr)
634 {
635 int32 c, ln = COM_MLINES;
636 uint16 ent;
637 
638 sim_activate (uptr, uptr->wait);                        /* continue poll */
639 c = sim_poll_kbd ();                                    /* get character */
640 if (c && !(c & (SCPE_BREAK|SCPE_KFLAG)))                /* error? */
641     return c;
642 if (!com_enab || (c & SCPE_BREAK))                      /* !enab, break? done */
643     return SCPE_OK;
644 if (coml_unit[ln].NEEDID)                               /* ID needed? */
645     return com_send_id (ln);
646 if ((c & SCPE_KFLAG) && ((c = c & 0177) != 0)) {        /* char input? */
647     if ((c == 0177) || (c == '\b')) {                   /* delete? */
648         ent = com_gettl_free (&com_inpq[ln]);           /* remove last char */
649         if (!coml_unit[ln].NOECHO)
650             sim_putchar (ent? '\b': '\a');
651         return SCPE_OK;
652         }
653     if (!com_queue_in (ln, c))                          /* add to inp queue */
654         return STOP_NOIFREE;
655     if (!coml_unit[ln].NOECHO) {                        /* echo enabled? */
656         if (sim_tt_outcvt (c, TT_MODE_7P) >= 0)         /* printable? */
657             sim_putchar (c);
658         if (c == '\r')                                  /* line end? */
659             sim_putchar ('\n');
660         }
661     }
662 return SCPE_OK;                                         /* set ATN if input */
663 }
664 
665 /* Unit service - receive side
666 
667    Poll all active lines for input
668    Poll for new connections */
669 
comi_svc(UNIT * uptr)670 t_stat comi_svc (UNIT *uptr)
671 {
672 int32 c, ln, t;
673 uint16 ent;
674 
675 if ((uptr->flags & UNIT_ATT) == 0)                      /* attached? */
676     return SCPE_OK;
677 t = sim_rtcn_calb (com_tps, TMR_COM);                   /* calibrate */
678 sim_activate (uptr, t);                                 /* continue poll */
679 if (!com_enab)                                          /* not enabled? exit */
680     return SCPE_OK;
681 ln = tmxr_poll_conn (&com_desc);                        /* look for connect */
682 if (ln >= 0) {                                          /* got one? */
683     com_ldsc[ln].rcve = 1;                              /* rcv enabled */
684     coml_unit[ln].CONN = 1;                             /* flag connected */
685     coml_unit[ln].NEEDID = 1;                           /* need ID */
686     coml_unit[ln].NOECHO = 0;                           /* echo enabled */
687     coml_unit[ln].INPP = 0;                             /* no input pending */
688     }
689 tmxr_poll_rx (&com_desc);                               /* poll for input */
690 for (ln = 0; ln < COM_MLINES; ln++) {                   /* loop thru mux */
691     if (com_ldsc[ln].conn) {                            /* connected? */
692         if (coml_unit[ln].NEEDID)
693             return com_send_id (ln);
694         c = tmxr_getc_ln (&com_ldsc[ln]);               /* get char */
695         if (c) {                                        /* any char? */
696             c = c & 0177;                               /* mask to 7b */
697             if ((c == 0177) || (c == '\b')) {           /* delete? */
698                 ent = com_gettl_free (&com_inpq[ln]);   /* remove last char */
699                 if (!coml_unit[ln].NOECHO)
700                     tmxr_putc_ln (&com_ldsc[ln], ent? '\b': '\a');
701                 return SCPE_OK;
702                 }
703             if (!com_queue_in (ln, c))                  /* queue char, err? */
704                 return STOP_NOIFREE;
705             if (com_ldsc[ln].xmte) {                    /* output enabled? */
706                 if (!coml_unit[ln].NOECHO) {            /* echo enabled? */
707                     if (sim_tt_outcvt (c, TT_MODE_7P) >= 0)
708                         tmxr_putc_ln (&com_ldsc[ln], c);
709                     if (c == '\r')                      /* add LF after CR */
710                         tmxr_putc_ln (&com_ldsc[ln], '\n');
711                     }
712                 tmxr_poll_tx (&com_desc);               /* poll xmt */
713                 }                                       /* end if enabled */
714             }                                           /* end if char */
715         }                                               /* end if conn */
716     else if (coml_unit[ln].CONN) {                      /* not conn, was conn? */
717         coml_unit[ln].CONN = 0;                         /* clear connected */
718         coml_unit[ln].NEEDID = 0;                       /* clear need id */
719         com_set_inpp (ln);                              /* input pending, ATN1 */
720         if (!com_new_puttl (&com_inpq[ln], COMI_HANGUP))/* hangup message */
721             return STOP_NOIFREE;
722         }
723     }                                                   /* end for */
724 return SCPE_OK;
725 }
726 
727 /* Unit service - console transmit */
728 
comto_svc(UNIT * uptr)729 t_stat comto_svc (UNIT *uptr)
730 {
731 uint32 ln = COM_MLINES;
732 uint32 c, c1;
733 
734 c = com_queue_out (ln, &c1);                            /* get character, cvt */
735 if (c)                                                  /* printable? output */
736     sim_putchar (c);
737 if (c1)                                                 /* second char? output */
738     sim_putchar (c1);
739 if (com_outq[ln].head == 0)                             /* line idle? */
740     ch9_set_atn (com_ch);                               /* set ATN1 */
741 else sim_activate (uptr, uptr->wait);                   /* next char */
742 return SCPE_OK;
743 }
744 
745 /* Unit service - transmit side */
746 
como_svc(UNIT * uptr)747 t_stat como_svc (UNIT *uptr)
748 {
749 uint32 c, c1;
750 int32 ln = uptr - coml_unit;                            /* line # */
751 
752 if (com_ldsc[ln].conn) {                                /* connected? */
753     if (com_ldsc[ln].xmte) {                            /* output enabled? */
754         c = com_queue_out (ln, &c1);                    /* get character, cvt */
755         if (c)                                          /* printable? output */
756             tmxr_putc_ln (&com_ldsc[ln], c);
757         if (c1)                                         /* print second */
758             tmxr_putc_ln (&com_ldsc[ln], c1);
759         }                                               /* end if */
760     tmxr_poll_tx (&com_desc);                           /* poll xmt */
761     if (com_outq[ln].head == 0)                         /* line idle? */
762         ch9_set_atn (com_ch);                           /* set ATN1 */
763     else sim_activate (uptr, uptr->wait);               /* next char */
764     }                                                   /* end if conn */
765 return SCPE_OK;
766 }
767 
768 /* Send ID sequence on input */
769 
com_send_id(uint32 ln)770 t_stat com_send_id (uint32 ln)
771 {
772 com_new_puttl (&com_inpq[ln], COMI_DIALUP);             /* input message: */
773 if (coml_unit[ln].flags & UNIT_K35)                     /* dialup, ID, endID */
774     com_new_puttl (&com_inpq[ln], COMI_K35);
775 else com_new_puttl (&com_inpq[ln], COMI_K37);
776 com_new_puttl (&com_inpq[ln], 0);
777 com_new_puttl (&com_inpq[ln], 0);
778 com_new_puttl (&com_inpq[ln], 0);
779 com_new_puttl (&com_inpq[ln], 0);
780 com_new_puttl (&com_inpq[ln], (uint16) (ln + COM_LBASE));
781 if (!com_new_puttl (&com_inpq[ln], COMI_ENDID))         /* make sure there */
782     return STOP_NOIFREE;                                /* was room for msg */
783 coml_unit[ln].NEEDID = 0;
784 com_set_inpp (ln);                                      /* input pending, ATN1 */
785 return SCPE_OK;
786 }
787 
788 /* Translate and queue input character */
789 
com_queue_in(uint32 ln,uint32 c)790 t_bool com_queue_in (uint32 ln, uint32 c)
791 {
792 uint16 out;
793 
794 if (c == com_intr) {
795     out = COMI_INTR;
796     com_set_inpp (ln);
797     }
798 else if (c == com_quit) {
799     out = COMI_QUIT;
800     com_set_inpp (ln);
801     }
802 else {
803     if (c == '\r')
804         com_set_inpp (ln);
805     if (coml_unit[ln].flags & UNIT_K35) {               /* KSR-35? */
806         if (islower (c))                                /* convert LC to UC */
807             c = toupper (c);
808         }
809     else c |= (com_epar[c]? COMI_PARITY: 0);            /* add even parity */
810     out = (~c) & 0377;                                  /* 1's complement */
811     }
812 return com_new_puttl (&com_inpq[ln], out);              /* input message */
813 }
814 
815 /* Retrieve and translate output character */
816 
com_queue_out(uint32 ln,uint32 * c1)817 uint32 com_queue_out (uint32 ln, uint32 *c1)
818 {
819 uint32 c, ent, raw;
820 
821 *c1 = 0;                                                /* assume non-printing */
822 if ((ent = com_gethd_free (&com_outq[ln])) == 0)        /* get character */
823     return 0;                                           /* nothing, exit */
824 raw = com_pkt[ent].data;                                /* get 12b character */
825 com_not_ret[ln]++;
826 if (raw == COMO_BITRPT) {                               /* insert delay? */
827     if (com_gethd_free (&com_outq[ln]))                 /* skip next char */
828          com_not_ret[ln]++;                             /* and count it */
829     return 0;
830     }
831 c = (~raw >> 1) & 0177;                                 /* remove start, parity */
832 if ((c >= 040) && (c != 0177)) {                        /* printable? */
833     if ((coml_unit[ln].flags & UNIT_K35) && islower (c))/* KSR-35 LC? */
834         c = toupper (c);                                /* cvt to UC */
835     return c;
836     }
837 switch (c) {
838 
839     case '\t': case '\f': case '\b': case '\a':         /* valid ctrls */
840         return c;
841 
842     case '\r':                                          /* carriage return? */
843         *c1 = '\n';                                     /* lf after cr */
844         return c;
845 
846     case '\n':                                          /* line feed? */
847         *c1 = '\n';                                     /* lf after cr */
848         return '\r';
849 
850     case 022:                                           /* DC2 */
851         coml_unit[ln].NOECHO = 1;
852         return 0;
853 
854     case 024:                                           /* DC4 */
855         coml_unit[ln].NOECHO = 0;
856         return 0;
857         }
858 
859 return 0;                                               /* ignore others */
860 }
861 
862 /* Generate completion message, if needed */
863 
com_gen_ccmp(uint32 ln)864 uint32 com_gen_ccmp (uint32 ln)
865 {
866 uint32 t;
867 
868 if ((t = com_not_ret[ln]) != 0) {                       /* chars not returned? */
869     if (t > COMI_CMAX)                                  /* limit to max */
870         t = COMI_CMAX;
871     com_not_ret[ln] -= t;                               /* keep count */
872     return COMI_COMP (t);                               /* gen completion msg */
873     }
874 return 0;
875 }
876 
877 /* Read and validate output buffer */
878 
com_getob(uint32 ch)879 t_uint64 com_getob (uint32 ch)
880 {
881 if (com_chob_v)                                         /* valid? clear */
882     com_chob_v = 0;
883 else if (!com_stop) {                                   /* not stopped? */
884     ch9_set_ioc (com_ch);                               /* IO check */
885     com_set_sns (COMS_ITMO);                            /* set sense bit */
886     }
887 return com_chob;
888 }
889 
890 /* Test whether input pending */
891 
com_test_inp(void)892 t_bool com_test_inp (void)
893 {
894 uint32 i;
895 
896 for (i = 0; i < COM_TLINES; i++) {
897     if ((com_not_ret[i] != 0) || coml_unit[i].INPP)
898         return TRUE;
899     }
900 return FALSE;
901 }
902 
903 /* Set input pending and attention */
904 
com_set_inpp(uint32 ln)905 void com_set_inpp (uint32 ln)
906 {
907 coml_unit[ln].INPP = 1;
908 ch9_set_atn (com_ch);
909 return;
910 }
911 
912 /* Test for done */
913 
com_qdone(uint32 ch)914 t_bool com_qdone (uint32 ch)
915 {
916 if (com_stop || !ch9_qconn (ch)) {                      /* stop or err disc? */
917     com_sta = 0;                                        /* ctrl is idle */
918     return TRUE;
919     }
920 return FALSE;
921 }
922 
923 /* Channel end */
924 
com_end(uint32 ch,uint32 fl,uint32 st)925 void com_end (uint32 ch, uint32 fl, uint32 st)
926 {
927 ch9_set_end (ch, fl);                                   /* set end */
928 ch_req |= REQ_CH (ch);
929 com_sta = st;                                           /* next state */
930 return;
931 }
932 
933 /* List routines - remove from head and free */
934 
com_gethd_free(LISTHD * lh)935 uint16 com_gethd_free (LISTHD *lh)
936 {
937 uint16 ent;
938 
939 if ((ent = com_gethd (lh)) != 0)
940     com_puttl (&com_free, ent);
941 return ent;
942 }
943 
944 /* Remove from tail and free */
945 
com_gettl_free(LISTHD * lh)946 uint16 com_gettl_free (LISTHD *lh)
947 {
948 uint16 ent;
949 
950 if ((ent = com_gethd (lh)) != 0)
951     com_puttl (&com_free, ent);
952 return ent;
953 }
954 
955 /* Get free entry and insert at tail */
956 
com_new_puttl(LISTHD * lh,uint16 val)957 t_bool com_new_puttl (LISTHD *lh, uint16 val)
958 {
959 uint16 ent;
960 
961 if ((ent = com_gethd (&com_free)) != 0) {
962     com_pkt[ent].data = val;
963     com_puttl (lh, ent);
964     return TRUE;
965     }
966 return FALSE;
967 }
968 
969 /* Remove from head */
970 
com_gethd(LISTHD * lh)971 uint16 com_gethd (LISTHD *lh)
972 {
973 uint16 ent;
974 
975 if ((ent = lh->head) != 0) {
976     lh->head = com_pkt[ent].next;
977     if (lh->head == 0)
978         lh->tail = 0;
979     }
980 else lh->tail = 0;
981 return ent;
982 }
983 
984 /* Remove from tail */
985 
com_gettl(LISTHD * lh)986 uint16 com_gettl (LISTHD *lh)
987 {
988 uint16 ent, next;
989 uint32 i;
990 
991 ent = lh->tail;
992 if (lh->head == lh->tail) {
993     lh->head = lh->tail = 0;
994     return ent;
995     }
996 next = lh->head;
997 for (i = 0; i < COM_PKTSIZ; i++) {
998     if (com_pkt[next].next == ent) {
999         com_pkt[next].next = 0;
1000         lh->tail = next;
1001         return ent;
1002         }
1003     }
1004 return 0;
1005 }
1006 
1007 /* Insert at tail */
1008 
com_puttl(LISTHD * lh,uint16 ent)1009 void com_puttl (LISTHD *lh, uint16 ent)
1010 {
1011 if (lh->tail == 0)
1012     lh->head = ent;
1013 else com_pkt[lh->tail].next = ent;
1014 com_pkt[ent].next = 0;
1015 lh->tail = ent;
1016 return;
1017 }
1018 
1019 /* Set flag in sense */
1020 
com_set_sns(t_uint64 stat)1021 void com_set_sns (t_uint64 stat)
1022 {
1023 com_sns |= stat;
1024 com_sns &= ~(COMS_PCHK|COMS_DCHK|COMS_EXCC);
1025 if (com_sns & COMS_PALL)
1026     com_sns |= COMS_PCHK;
1027 if (com_sns & COMS_DALL)
1028     com_sns |= COMS_DCHK;
1029 if (com_sns & COMS_EALL)
1030     com_sns |= COMS_EXCC;
1031 return;
1032 }
1033 
1034 /* Reset routine */
1035 
com_reset(DEVICE * dptr)1036 t_stat com_reset (DEVICE *dptr)
1037 {
1038 uint32 i;
1039 
1040 if (dptr->flags & DEV_DIS) {                            /* disabled? */
1041     com_dev.flags = com_dev.flags | DEV_DIS;            /* disable lines */
1042     coml_dev.flags = coml_dev.flags | DEV_DIS;
1043     }
1044 else {
1045     com_dev.flags = com_dev.flags & ~DEV_DIS;           /* enable lines */
1046     coml_dev.flags = coml_dev.flags & ~DEV_DIS;
1047     }
1048 sim_activate (&com_unit[COM_CIU], com_unit[COM_CIU].wait); /* console */
1049 sim_cancel (&com_unit[COM_PLU]);
1050 sim_cancel (&com_unit[COM_CHU]);
1051 if (com_unit[COM_PLU].flags & UNIT_ATT) {               /* master att? */
1052     int32 t = sim_rtcn_init (com_unit[COM_PLU].wait, TMR_COM);
1053     sim_activate (&com_unit[COM_PLU], t);
1054     }
1055 com_enab = 0;
1056 com_sns = 0;
1057 com_msgn = 0;
1058 com_sta = 0;
1059 com_chob = 0;
1060 com_chob_v = 0;
1061 com_stop = 0;
1062 com_bptr = 0;
1063 com_blim = 0;
1064 for (i = 0; i < COM_BUFSIZ; i++)
1065     com_buf[i] = 0;
1066 for (i = 0; i < COM_TLINES; i++) {                      /* init lines */
1067     com_inpq[i].head = 0;
1068     com_inpq[i].tail = 0;
1069     com_outq[i].head = 0;
1070     com_outq[i].tail = 0;
1071     com_reset_ln (i);
1072     }
1073 com_pkt[0].next = 0;                                    /* init free list */
1074 for (i = 1; i < COM_PKTSIZ; i++) {
1075     com_pkt[i].next = i + 1;
1076     com_pkt[i].data = 0;
1077     }
1078 com_pkt[COM_PKTSIZ - 1].next = 0;                       /* end of free list */
1079 com_free.head = 1;
1080 com_free.tail = COM_PKTSIZ - 1;
1081 coml_unit[COM_MLINES].CONN = 1;                         /* console always conn */
1082 coml_unit[COM_MLINES].NEEDID = 1;
1083 return SCPE_OK;
1084 }
1085 
1086 /* Attach master unit */
1087 
com_attach(UNIT * uptr,char * cptr)1088 t_stat com_attach (UNIT *uptr, char *cptr)
1089 {
1090 t_stat r;
1091 
1092 r = tmxr_attach (&com_desc, uptr, cptr);                /* attach */
1093 if (r != SCPE_OK)                                       /* error */
1094     return r;
1095 sim_rtcn_init (uptr->wait, TMR_COM);
1096 sim_activate (uptr, 100);                               /* quick poll */
1097 return SCPE_OK;
1098 }
1099 
1100 /* Detach master unit */
1101 
com_detach(UNIT * uptr)1102 t_stat com_detach (UNIT *uptr)
1103 {
1104 uint32 i;
1105 t_stat r;
1106 
1107 r = tmxr_detach (&com_desc, uptr);                      /* detach */
1108 for (i = 0; i < COM_MLINES; i++)                        /* disable rcv */
1109     com_ldsc[i].rcve = 0;
1110 sim_cancel (uptr);                                      /* stop poll */
1111 return r;
1112 }
1113 
1114 /* Reset an individual line */
1115 
com_reset_ln(uint32 ln)1116 void com_reset_ln (uint32 ln)
1117 {
1118 while (com_gethd_free (&com_inpq[ln])) ;
1119 while (com_gethd_free (&com_outq[ln])) ;
1120 com_not_ret[ln] = 0;
1121 coml_unit[ln].NEEDID = 0;
1122 coml_unit[ln].NOECHO = 0;
1123 coml_unit[ln].INPP = 0;
1124 sim_cancel (&coml_unit[ln]);
1125 if ((ln < COM_MLINES) && (com_ldsc[ln].conn == 0))
1126     coml_unit[ln].CONN = 0;
1127 return;
1128 }
1129 
1130 /* Special show commands */
1131 
com_show_qsumm(FILE * st,LISTHD * lh,char * name)1132 uint32 com_show_qsumm (FILE *st, LISTHD *lh, char *name)
1133 {
1134 uint32 i, next;
1135 
1136 next = lh->head;
1137 for (i = 0; i < COM_PKTSIZ; i++) {
1138     if (next == 0) {
1139         if (i == 0)
1140             fprintf (st, "%s is empty\n", name);
1141         else if (i == 1)
1142             fprintf (st, "%s has 1 entry\n", name);
1143         else fprintf (st, "%s has %d entries\n", name, i);
1144         return i;
1145         }
1146     next = com_pkt[next].next;
1147     }
1148 fprintf (st, "%s is corrupt\n", name);
1149 return 0;
1150 }
1151 
com_show_char(FILE * st,uint32 ch)1152 void com_show_char (FILE *st, uint32 ch)
1153 {
1154 uint32 c;
1155 
1156 fprintf (st, "%03o", ch);
1157 c = (~ch) & 0177;
1158 if (((ch & 07400) == 0) && (c >= 040) && (c != 0177))
1159     fprintf (st, "[%c]", c);
1160 return;
1161 }
1162 
com_show_freeq(FILE * st,UNIT * uptr,int32 val,void * desc)1163 t_stat com_show_freeq (FILE *st, UNIT *uptr, int32 val, void *desc)
1164 {
1165 com_show_qsumm (st, &com_free, "Free queue");
1166 return SCPE_OK;
1167 }
1168 
com_show_oneq(FILE * st,UNIT * uptr,int32 val,void * desc)1169 t_stat com_show_oneq (FILE *st, UNIT *uptr, int32 val, void *desc)
1170 {
1171 uint32 entc, ln, i, next;
1172 LISTHD *lh;
1173 char name[20];
1174 
1175 ln = uptr - coml_dev.units;
1176 sprintf (name, val? "Output queue %d": "Input queue %d", ln);
1177 lh = val? &com_outq[ln]: &com_inpq[ln];
1178 if ((entc = com_show_qsumm (st, lh, name))) {
1179     for (i = 0, next = lh->head; next != 0;
1180          i++, next = com_pkt[next].next) {
1181         if ((i % 8) == 0)
1182             fprintf (st, "%d:\t", i);
1183         com_show_char (st, com_pkt[next].data >> (val? 1: 0));
1184         fputc ((((i % 8) == 7)? '\n': '\t'), st);
1185         }
1186     if (i % 8)
1187         fputc ('\n', st);
1188     }
1189 return SCPE_OK;
1190 }
1191 
com_show_allq(FILE * st,UNIT * uptr,int32 val,void * desc)1192 t_stat com_show_allq (FILE *st, UNIT *uptr, int32 val, void *desc)
1193 {
1194 uint32 i;
1195 
1196 for (i = 0; i < COM_TLINES; i++)
1197     com_show_oneq (st, coml_dev.units + i, val, desc);
1198 return SCPE_OK;
1199 }
1200 
com_show_ctrl(FILE * st,UNIT * uptr,int32 val,void * desc)1201 t_stat com_show_ctrl (FILE *st, UNIT *uptr, int32 val, void *desc)
1202 {
1203 if (!com_enab)
1204     fprintf (st, "Controller is not initialized\n");
1205 if (val & COMR_FQ)
1206     com_show_freeq (st, uptr, 0, desc);
1207 if (val & COMR_IQ)
1208     com_show_allq (st, uptr, 0, desc);
1209 if (val & COMR_OQ)
1210     com_show_allq (st, uptr, 1, desc);
1211 return SCPE_OK;
1212 }
1213