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