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