1 /* i7094_com.c: IBM 7094 7750 communications interface simulator
3 Copyright (c) 2005-2006, Robert M Supnik
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:
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
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.
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.
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.
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
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.
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.
58 Note that the simulator console is mapped in as line n+1.
61 #include "i7094_defs.h"
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 */
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)
76 #define CONN u3 /* line is connected */
77 #define NEEDID u4 /* need to send ID */
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 */
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 */
91 /* Communications input */
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 */
109 /* Communications output */
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)
120 /* Status word (60b) */
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
137 /* Report variables */
139 #define COMR_FQ 1 /* free queue */
140 #define COMR_IQ 2 /* input queue */
141 #define COMR_OQ 4 /* output queue */
143 /* List heads and entries */
155 /* The 7750 character buffer is maintained as linked lists. The lists are:
159 outq[ln] output queue for line ln
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.
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. */
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 */
190 /* Even parity truth table */
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
211 extern uint32 ch_req
;
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
);
248 /* COM data structures
250 com_dev COM device descriptor
251 com_unit COM unit descriptor
252 com_reg COM register list
253 com_mod COM modifiers list
256 DIB com_dib
= { &com_chsel
, &com_chwr
};
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
}
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
},
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 },
307 "COM", com_unit
, com_reg
, com_mod
,
309 &tmxr_ex
, &tmxr_dep
, &com_reset
,
310 NULL
, &com_attach
, &com_detach
,
311 &com_dib
, DEV_NET
| DEV_DIS
314 /* COMLL data structures
316 coml_dev COML device descriptor
317 coml_unit COML unit descriptor
318 coml_reg COML register list
319 coml_mod COML modifiers list
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
},
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
},
372 { URDATA (TIME
, coml_unit
[0].wait
, 10, 24, 0,
373 COM_TLINES
, REG_NZ
+ PV_LEFT
) },
378 "COML", coml_unit
, coml_reg
, coml_mod
,
379 COM_TLINES
, 10, 31, 1, 16, 8,
380 NULL
, NULL
, &com_reset
,
385 /* COM: channel select */
387 t_stat
com_chsel (uint32 ch
, uint32 sel
, uint32 unit
)
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 */
396 switch (sel
) { /* case on select */
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
);
404 case CHSL_SNS
: /* sense */
405 sim_activate (&com_unit
[COM_SNS
], com_unit
[COM_SNS
].wait
);
408 case CHSL_CTL
: /* control */
413 com_stop
= 0; /* clear stop */
414 com_sta
= sel
; /* set initial state */
418 /* Channel write, from 7909 channel program */
420 t_stat
com_chwr (uint32 ch
, t_uint64 val
, uint32 stopf
)
422 if (stopf
) com_stop
= 1;
424 com_chob
= val
; /* store data */
425 com_chob_v
= 1; /* set valid */
430 /* Unit service - SNS */
432 t_stat
coms_svc (UNIT
*uptr
)
436 switch (com_sta
) { /* case on state */
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
;
446 com_sta
= CHSL_SNS
|CHSL_2ND
; /* 2nd state */
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 */
457 dat
= com_buf
[com_bptr
++]; /* get word */
458 if (!com_stop
) ch9_req_rd (com_ch
, dat
); /* send wd to chan */
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 */
467 sim_activate (uptr
, uptr
->wait
); /* sched next */
471 /* Unit service - channel program */
473 t_stat
comc_svc (UNIT
*uptr
)
475 uint32 i
, j
, k
, ccnt
, ln
, uln
, ent
;
479 switch (com_sta
) { /* case on state */
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));
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
;
496 com_bptr
= 0; /* init buf ptr */
497 com_blim
= j
+ 1; /* save buf size */
498 com_sta
= CHSL_RDS
|CHSL_2ND
; /* next state */
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 */
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 */
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 */
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 */
532 else if (dat
& COMO_LINCTL
) { /* control message? */
533 ln
= COMO_GETLN (dat
); /* line number */
534 if (ln
>= (COM_TLINES
+ COM_LBASE
)) /* invalid line? */
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 */
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
))
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 */
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? */
559 if ((com_buf
[0] & COMO_LIN12B
) && /* 12b message? */
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 */
569 sim_activate (&coml_unit
[uln
], coml_unit
[uln
].wait
);
571 com_end (com_ch
, 0, CHSL_WRS
|CHSL_4TH
); /* end, last state */
573 else if (!com_stop
) ch_req
|= REQ_CH (com_ch
); /* request channel */
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 */
586 sim_activate (uptr
, uptr
->wait
);
590 /* Unit service - console receive - always running, even if device is not */
592 t_stat
comti_svc (UNIT
*uptr
)
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');
608 return com_test_atn (com_ch
); /* set ATN if input */
611 /* Unit service - receive side
613 Poll all active lines for input
614 Poll for new connections */
616 t_stat
comi_svc (UNIT
*uptr
)
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 */
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 */
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 */
655 return com_test_atn (com_ch
); /* set ATN if input */
658 /* Unit service - console transmit */
660 t_stat
comto_svc (UNIT
*uptr
)
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 */
675 /* Unit service - transmit side */
677 t_stat
como_svc (UNIT
*uptr
)
680 int32 ln
= uptr
- coml_unit
; /* line # */
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 */
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 */
698 /* Send ID sequence on input */
700 t_stat
com_send_id (uint32 ln
)
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
);
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;
717 /* Translate and queue input character */
719 t_stat
com_queue_in (uint32 ln
, uint32 c
)
723 if (c
== com_intr
) out
= COMI_INTR
;
724 else if (c
== com_quit
) out
= COMI_QUIT
;
726 if (coml_unit
[ln
].flags
& UNIT_K35
) { /* KSR-35? */
727 if (islower (c
)) c
= toupper (c
); /* convert LC to UC */
729 else c
|= (com_epar
[c
]? COMI_PARITY
: 0); /* add even parity */
730 out
= (~c
) & 0377; /* 1's complement */
732 if (!com_inp_msg (ln
, out
)) return STOP_NOIFREE
; /* input message */
736 /* Retrieve and translate output character */
738 uint32
com_queue_out (uint32 ln
, uint32
*c1
)
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 */
747 if (raw
== COMO_BITRPT
) { /* insert delay? */
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 */
760 case '\t': case '\f': case '\b': case '\a': /* valid ctrls */
763 case '\r': /* carriage return? */
764 if (coml_unit
[ln
].flags
& UNIT_K35
) /* KSR-35? */
765 *c1
= '\n'; /* lf after cr */
768 case '\n': /* line feed? */
769 if (!(coml_unit
[ln
].flags
& UNIT_K35
)) { /* KSR-37? */
770 *c1
= '\n'; /* lf after cr */
773 return c
; /* print lf */
776 if (!(com_unit
[ln
].flags
& UNIT_K35
)) { /* KSR-37? */
777 com_skip_outc (ln
); /* skip next */
778 return '\n'; /* print lf */
783 if (!(com_unit
[ln
].flags
& UNIT_K35
)) /* KSR-37? */
784 com_skip_outc (ln
); /* skip next */
788 return 0; /* ignore others */
791 /* Generate completion message, if needed */
793 t_stat
com_send_ccmp (uint32 ln
)
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 */
806 /* Skip next char in output queue */
808 void com_skip_outc (uint32 ln
)
810 if (com_gethd_free (&com_outq
[ln
])) com_not_ret
[ln
]++; /* count it */
814 /* Read and validate output buffer */
816 t_uint64
com_getob (uint32 ch
)
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 */
826 /* Set attention if input pending */
828 t_stat
com_test_atn (uint32 ch
)
830 if (com_inpq
.head
) ch9_set_atn (ch
);
836 t_bool
com_qdone (uint32 ch
)
838 if (com_stop
|| !ch9_qconn (ch
)) { /* stop or err disc? */
839 com_sta
= 0; /* ctrl is idle */
847 void com_end (uint32 ch
, uint32 fl
, uint32 st
)
849 ch9_set_end (ch
, fl
); /* set end */
850 ch_req
|= REQ_CH (ch
);
851 com_sta
= st
; /* next state */
855 /* List routines - remove from head and free */
857 uint16
com_gethd_free (LISTHD
*lh
)
861 if ((ent
= com_gethd (lh
)) != 0)
862 com_puttl (&com_free
, ent
);
866 /* Get free entry and insert at tail */
868 t_bool
com_new_puttl (LISTHD
*lh
, uint16 val
)
872 if ((ent
= com_gethd (&com_free
)) != 0) {
873 com_pkt
[ent
].data
= val
;
880 /* Remove from head */
882 uint16
com_gethd (LISTHD
*lh
)
886 if ((ent
= lh
->head
) != 0) {
887 lh
->head
= com_pkt
[ent
].next
;
888 if (lh
->head
== 0) lh
->tail
= 0;
896 void com_puttl (LISTHD
*lh
, uint16 ent
)
898 if (lh
->tail
== 0) lh
->head
= ent
;
899 else com_pkt
[lh
->tail
].next
= ent
;
900 com_pkt
[ent
].next
= 0;
905 /* Insert line and message into input queue */
907 t_bool
com_inp_msg (uint32 ln
, uint16 msg
)
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
);
919 com_puttl (&com_free
, ent1
); /* free 1st */
921 return FALSE
; /* failed */
924 /* Set flag in sense */
926 void com_set_sns (t_uint64 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
;
938 t_stat
com_reset (DEVICE
*dptr
)
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
;
947 com_dev
.flags
= com_dev
.flags
& ~DEV_DIS
; /* enable lines */
948 coml_dev
.flags
= coml_dev
.flags
& ~DEV_DIS
;
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
);
966 for (i
= 0; i
< COM_BUFSIZ
; i
++) com_buf
[i
] = 0;
967 com_inpq
.head
= 0; /* init queues */
969 for (i
= 0; i
< COM_TLINES
; i
++) {
970 com_outq
[i
].head
= 0;
971 com_outq
[i
].tail
= 0;
974 com_pkt
[0].next
= 0; /* init free list */
975 for (i
= 1; i
< COM_PKTSIZ
; i
++) {
976 com_pkt
[i
].next
= i
+ 1;
979 com_pkt
[COM_PKTSIZ
- 1].next
= 0; /* end of free list */
981 com_free
.tail
= COM_PKTSIZ
- 1;
982 coml_unit
[COM_MLINES
].CONN
= 1; /* console always conn */
983 coml_unit
[COM_MLINES
].NEEDID
= 1;
987 /* Attach master unit */
989 t_stat
com_attach (UNIT
*uptr
, char *cptr
)
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 */
1000 /* Detach master unit */
1002 t_stat
com_detach (UNIT
*uptr
)
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 */
1013 /* Show summary processor */
1015 t_stat
com_summ (FILE *st
, UNIT
*uptr
, int32 val
, void *desc
)
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
);
1025 /* SHOW CONN/STAT processor */
1027 t_stat
com_show (FILE *st
, UNIT
*uptr
, int32 val
, void *desc
)
1031 for (i
= t
= 0; i
< COM_MLINES
; i
++) t
= t
+ (com_ldsc
[i
].conn
!= 0);
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
);
1040 else fprintf (st
, "all disconnected\n");
1044 /* Reset an individual line */
1046 void com_reset_ln (uint32 ln
)
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;
1055 /* Special show commands */
1057 uint32
com_show_qsumm (FILE *st
, LISTHD
*lh
, char *name
)
1062 for (i
= 0; i
< COM_PKTSIZ
; i
++) {
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
);
1069 next
= com_pkt
[next
].next
;
1071 fprintf (st
, "%s is corrupt\n", name
);
1075 void com_show_char (FILE *st
, uint32 ch
)
1079 fprintf (st
, "%03o", ch
);
1081 if (((ch
& 07400) == 0) && (c
>= 040) && (c
!= 0177)) fprintf (st
, "[%c]", c
);
1085 t_stat
com_show_freeq (FILE *st
, UNIT
*uptr
, int32 val
, void *desc
)
1087 com_show_qsumm (st
, &com_free
, "Free queue");
1091 t_stat
com_show_inq (FILE *st
, UNIT
*uptr
, int32 val
, void *desc
)
1093 uint32 entc
, ln
, i
, next
;
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
;
1102 fprintf (st
, "Line number without data\n");
1105 fprintf (st
, "%d/", ln
);
1106 com_show_char (st
, com_pkt
[next
].data
);
1107 fputc ((((i
% 4) == 3)? '\n': '\t'), st
);
1109 if (i
% 4) fputc ('\n', st
);
1114 t_stat
com_show_outq (FILE *st
, UNIT
*uptr
, int32 val
, void *desc
)
1116 uint32 entc
, ln
, i
, next
;
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
);
1128 if (i
% 8) fputc ('\n', st
);
1133 t_stat
com_show_aoutq (FILE *st
, UNIT
*uptr
, int32 val
, void *desc
)
1137 for (i
= 0; i
< COM_TLINES
; i
++)
1138 com_show_outq (st
, com_dev
.units
+ i
, 1, desc
);
1142 t_stat
com_show_ctrl (FILE *st
, UNIT
*uptr
, int32 val
, void *desc
)
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
);