1 /*
2 * Copyright (c) 2007-2013 Michael Mondy
3 * Copyright (c) 2012-2016 Harry Reed
4 * Copyright (c) 2013-2016 Charles Anthony
5 * Copyright (c) 2021 The DPS8M Development Team
6 *
7 * All rights reserved.
8 *
9 * This software is made available under the terms of the ICU
10 * License, version 1.8.1 or later. For more details, see the
11 * LICENSE.md file at the top-level directory of this distribution.
12 */
13
14 // There is a lurking bug in fnpProcessEvent(). A second 'input' messages
15 // from a particular line could be placed in mailbox beforme the first is
16 // processed. This could lead to the messages being picked up by MCS in
17 // the wrong order. The quick fix is to use just a single mbx; a better
18 // is to track the line # associated with an busy mailbox, and requeue
19 // any message that from a line that is in a busy mailbox. I wonder how
20 // the real DN355 dealt with this?
21
22 ////
23 //
24 // 3270 station to CS data flow
25 //
26 // On connection:
27 //
28 // Station number is assigned.
29 // Connection is saved:
30 // fnpData.ibm3270ctlr[ASSUME0].stations[stn_no].client = client;
31 // Read callback regeistered.
32 // client->data->read_cb = fnpuv_3270_readcb;
33 // Telnet negotiation is started:
34 // client->data->telnetp = ltnConnect3270 (client);
35 // DPS8 banner sent:
36 // fnp3270ConnectPrompt (client);
37 //
38 // Read data callback:
39 //
40 // fnpuv_3270_readcb calls process3270Input.
41 // process3270Input appends the data to stn_in_buffer.
42 //
43 // Read EOT callback:
44 //
45 // evHandler calls fnpuv_recv_eor().
46 // fnpuv_recv_eor() call fnpRecvEOR ().
47 // fnpRecvEOR():
48 // fnpData.ibm3270ctlr[ASSUME0].stations[p->stationNo].EORReceived = true;
49 // fnpData.ibm3270ctlr[ASSUME0].stations[p->stationNo].hdr_sent = false;
50 //
51 // fnpProcessEvent() event loop calls fnp_process_3270_event().
52 // fnp_process_3270_event():
53 // if polling
54 // for each station
55 // if (stnp->EORReceived)
56 // stnp->EORReceived = false;
57 // ctlrp->sending_stn_in_buffer = true;
58 // fnpuv3270Poll (false);
59 //
60 // fnpProcessEvent() event loop calls fnp_process_3270_event().
61 // fnp_process_3270_event():
62 // if (fnpData.ibm3270ctlr[ASSUME0].sending_stn_in_buffer)
63 // send_stn_in_buffer ();
64
65 #define ASSUME0 0
66
67 #include <stdio.h>
68 #include <ctype.h>
69 #include "dps8.h"
70 #include "dps8_sys.h"
71 #include "dps8_faults.h"
72 #include "dps8_scu.h"
73 #include "dps8_iom.h"
74 #include "dps8_cable.h"
75 #include "dps8_cpu.h"
76 #include "dps8_fnp2.h"
77 #include "fnptelnet.h"
78 #include "fnpuv.h"
79 #include "dps8_utils.h"
80 #include "utlist.h"
81 #include "uthash.h"
82
83 #include "sim_defs.h"
84 #include "sim_tmxr.h"
85
86 #ifndef CROSS_MINGW64
87 # ifndef CROSS_MINGW32
88 # include <regex.h>
89 # endif /* ifndef CROSS_MINGW32 */
90 #endif /* ifndef CROSS_MINGW64 */
91
92 #define DBG_CTR 1
93
94 #if defined(THREADZ) || defined(LOCKLESS)
95 # include "threadz.h"
96 #endif /* defined(THREADZ) || defined(LOCKLESS) */
97
98 static t_stat fnpShowConfig (FILE *st, UNIT *uptr, int val, const void *desc);
99 static t_stat fnpSetConfig (UNIT * uptr, int value, const char * cptr, void * desc);
100 static t_stat fnpShowStatus (FILE *st, UNIT *uptr, int val, const void *desc);
101 static t_stat fnpShowNUnits (FILE *st, UNIT *uptr, int val, const void *desc);
102 static t_stat fnpSetNUnits (UNIT * uptr, int32 value, const char * cptr, void * desc);
103 static t_stat fnpShowIPCname (FILE *st, UNIT *uptr, int val, const void *desc);
104 static t_stat fnpSetIPCname (UNIT * uptr, int32 value, const char * cptr, void * desc);
105 static t_stat fnpShowService (FILE *st, UNIT *uptr, int val, const void *desc);
106 static t_stat fnpSetService (UNIT * uptr, int32 value, const char * cptr, void * desc);
107 static t_stat fnpShowFW (FILE *st, UNIT *uptr, int val, const void *desc);
108 static t_stat fnpSetFW (UNIT * uptr, int32 value, const char * cptr, void * desc);
109 static t_stat fnp_show_device_name (UNUSED FILE * st, UNIT * uptr,
110 UNUSED int val, UNUSED const void * desc);
111 static t_stat fnp_set_device_name (UNIT * uptr, UNUSED int32 value,
112 const char * cptr, UNUSED void * desc);
113
114 static int findMbx (uint fnpUnitIdx);
115
116 #define N_FNP_UNITS 1 // default
117
118 UNIT fnp_unit [N_FNP_UNITS_MAX] = {
119 #ifdef NO_C_ELLIPSIS
120 { UDATA (NULL, UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
121 { UDATA (NULL, UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
122 { UDATA (NULL, UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
123 { UDATA (NULL, UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
124 { UDATA (NULL, UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
125 { UDATA (NULL, UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
126 { UDATA (NULL, UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
127 { UDATA (NULL, UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
128 { UDATA (NULL, UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
129 { UDATA (NULL, UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
130 { UDATA (NULL, UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
131 { UDATA (NULL, UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
132 { UDATA (NULL, UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
133 { UDATA (NULL, UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
134 { UDATA (NULL, UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
135 { UDATA (NULL, UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL }
136 #else
137 [0 ... N_FNP_UNITS_MAX - 1] = {
138 UDATA (NULL, UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL
139 }
140 #endif
141 };
142
143 static DEBTAB fnpDT [] =
144 {
145 { "TRACE", DBG_TRACE, NULL },
146 { "NOTIFY", DBG_NOTIFY, NULL },
147 { "INFO", DBG_INFO, NULL },
148 { "ERR", DBG_ERR, NULL },
149 { "WARN", DBG_WARN, NULL },
150 { "DEBUG", DBG_DEBUG, NULL },
151 { "ALL", DBG_ALL, NULL }, // don't move as it messes up DBG message
152 { NULL, 0, NULL }
153 };
154
155 static MTAB fnpMod [] =
156 {
157 {
158 MTAB_unitonly_value,
159 0, /* match */
160 "CONFIG", /* print string */
161 "CONFIG", /* match string */
162 fnpSetConfig, /* validation routine */
163 fnpShowConfig, /* display routine */
164 NULL, /* value descriptor */
165 NULL // help string
166 },
167
168 {
169 MTAB_unitonly_value,
170 0, /* match */
171 "STATUS", /* print string */
172 "STATUS", /* match string */
173 NULL, /* validation routine */
174 fnpShowStatus, /* display routine */
175 NULL, /* value descriptor */
176 NULL // help string
177 },
178
179 {
180 MTAB_dev_value,
181 0, /* match */
182 "NUNITS", /* print string */
183 "NUNITS", /* match string */
184 fnpSetNUnits, /* validation routine */
185 fnpShowNUnits, /* display routine */
186 "Number of FNP units in the system", /* value descriptor */
187 NULL // help
188 },
189 {
190 MTAB_unit_valr_nouc,
191 0, /* match */
192 "IPC_NAME", /* print string */
193 "IPC_NAME", /* match string */
194 fnpSetIPCname, /* validation routine */
195 fnpShowIPCname, /* display routine */
196 "Set the device IPC name", /* value descriptor */
197 NULL // help
198 },
199 {
200 MTAB_unit_valr_nouc,
201 0, /* match */
202 "SERVICE", /* print string */
203 "SERVICE", /* match string */
204 fnpSetService, /* validation routine */
205 fnpShowService, /* display routine */
206 "Set the device IPC name", /* value descriptor */
207 NULL // help
208 },
209
210 {
211 MTAB_dev_valr_noshow,
212 0, /* match */
213 "FW", /* print string */
214 "FW", /* match string */
215 fnpSetFW, /* validation routine */
216 fnpShowFW, /* display routine */
217 "Edit firewall", /* value descriptor */
218 NULL // help
219 },
220 {
221 MTAB_XTD | MTAB_VUN | MTAB_VALR | MTAB_NC, /* mask */
222 0, /* match */
223 "NAME", /* print string */
224 "NAME", /* match string */
225 fnp_set_device_name, /* validation routine */
226 fnp_show_device_name, /* display routine */
227 "Set the device name", /* value descriptor */
228 NULL // help
229 },
230 MTAB_eol
231 };
232
233 #define FNP_UNIT_IDX(uptr) ((uptr) - fnp_unit)
234
235 static t_stat fnpReset (DEVICE * dptr);
236
237 DEVICE fnp_dev = {
238 "FNP", /* name */
239 fnp_unit, /* units */
240 NULL, /* registers */
241 fnpMod, /* modifiers */
242 N_FNP_UNITS, /* #units */
243 10, /* address radix */
244 31, /* address width */
245 1, /* address increment */
246 8, /* data radix */
247 9, /* data width */
248 NULL, /* examine routine */
249 NULL, /* deposit routine */
250 fnpReset, /* reset routine */
251 NULL, /* boot routine */
252 NULL, /* attach routine */
253 NULL, /* detach routine */
254 NULL, /* context */
255 DEV_DEBUG, /* flags */
256 0, /* debug control flags */
257 fnpDT, /* debug flag names */
258 NULL, /* memory size change */
259 NULL, /* logical name */
260 NULL, // attach help
261 NULL, // help
262 NULL, // help context
263 NULL, // device description
264 NULL
265 };
266
267 t_fnpData fnpData;
268
269 #define l_putbits36_1 putbits36_1
270 #define l_putbits36_3 putbits36_3
271 #define l_putbits36_6 putbits36_6
272 #define l_putbits36_9 putbits36_9
273 #define l_putbits36_12 putbits36_12
274 #define l_putbits36_18 putbits36_18
275
setTIMW(uint iom_unit_idx,uint chan,word24 mailboxAddress,int mbx)276 void setTIMW (uint iom_unit_idx, uint chan, word24 mailboxAddress, int mbx)
277 {
278 word24 timwAddress = mailboxAddress + TERM_INPT_MPX_WD;
279 word36 data;
280 iom_direct_data_service (iom_unit_idx, chan, timwAddress, & data, direct_read_clear);
281 l_putbits36_1 (& data, (uint) mbx, 1);
282 iom_direct_data_service (iom_unit_idx, chan, timwAddress, & data, direct_store);
283 }
284
285 #ifdef SCUMEM
get_scu_unit_idx_iom(uint fnp_unit_idx,word24 addr,word24 * offset)286 uint get_scu_unit_idx_iom (uint fnp_unit_idx, word24 addr, word24 * offset)
287 {
288 uint ctlr_port_num = 0; // FNPs are single ported
289 uint iom_unit_idx = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].iom_unit_idx;
290 // XXX can query_IOM_SCU_bank_map return -1 here? if so, what to do?
291 // The address is known to reside in the bootload SCU; we can't get to here unless that is working.
292 uint scu_unit_num = (uint) query_IOM_SCU_bank_map (iom_unit_idx, addr, offset);
293 uint scu_unit_idx = cables->iom_to_scu[iom_unit_idx][scu_unit_num].scu_unit_idx;
294 return scu_unit_idx;
295 }
296 #endif
297
298 //
299 // Once-only initialization
300 //
301
fnpInit(void)302 void fnpInit(void)
303 {
304 // 0 sets set service to service_undefined
305 memset(& fnpData, 0, sizeof(fnpData));
306 fnpData.telnet_address = strdup ("0.0.0.0");
307 fnpData.telnet_port = 6180;
308 fnpData.telnet3270_port = 3270;
309 fnpTelnetInit ();
310 fnp3270Init ();
311 }
312
fnpReset(UNUSED DEVICE * dptr)313 static t_stat fnpReset (UNUSED DEVICE * dptr)
314 {
315 #if 0
316 for (int i = 0; i < (int) dptr -> numunits; i ++)
317 {
318 //sim_cancel (& fnp_unit [i]);
319 }
320 #endif
321 return SCPE_OK;
322 }
323
324 //
325 // Locate an available fnp_submailbox
326 //
327
findMbx(uint fnpUnitIdx)328 static int findMbx (uint fnpUnitIdx)
329 {
330 struct fnpUnitData_s * fudp = & fnpData.fnpUnitData [fnpUnitIdx];
331 for (uint i = 0; i < 4; i ++)
332 if (! fudp -> fnpMBXinUse [i])
333 return (int) i;
334 return -1;
335 }
336
notifyCS(uint mbx,int fnp_unit_idx,int lineno)337 static void notifyCS (uint mbx, int fnp_unit_idx, int lineno)
338 {
339 struct fnpUnitData_s * fudp = & fnpData.fnpUnitData [fnp_unit_idx];
340 word24 fsmbx = fudp->mailboxAddress + FNP_SUB_MBXES + mbx*FNP_SUB_MBX_SIZE;
341
342 uint ctlr_port_num = 0; // FNPs are single ported
343 uint iom_unit_idx = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].iom_unit_idx;
344 uint chan_num = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].chan_num;
345
346 word36 data = 0;
347 l_putbits36_3 (& data, 0, (word3) fnp_unit_idx); // dn355_no XXX
348 l_putbits36_1 (& data, 8, 1); // is_hsla XXX
349 l_putbits36_3 (& data, 9, 0); // la_no XXX
350 l_putbits36_6 (& data, 12, (word6) lineno); // slot_no XXX
351 l_putbits36_18 (& data, 18, 256); // blocks available XXX
352 iom_direct_data_service (iom_unit_idx, chan_num, fsmbx+WORD1, & data, direct_store);
353
354 fudp->fnpMBXinUse [mbx] = true;
355
356 setTIMW (iom_unit_idx, chan_num, fudp->mailboxAddress, (int)(mbx + 8));
357
358 fudp->lineWaiting [mbx] = true;
359 fudp->fnpMBXlineno [mbx] = lineno;
360 struct t_line * linep = & fudp->MState.line[lineno];
361 linep->waitForMbxDone=true;
362
363 sim_debug (DBG_TRACE, & fnp_dev, "[%d]notifyCS %d %d\n", lineno, mbx, chan_num);
364 }
365
fnp_rcd_ack_echnego_init(uint mbx,int fnp_unit_idx,int lineno)366 static void fnp_rcd_ack_echnego_init (uint mbx, int fnp_unit_idx, int lineno)
367 {
368 sim_debug (DBG_TRACE, & fnp_dev, "[%d]rcd ack_echnego_init\n", lineno);
369 struct fnpUnitData_s * fudp = & fnpData.fnpUnitData [fnp_unit_idx];
370 word24 fsmbx = fudp->mailboxAddress + FNP_SUB_MBXES + mbx*FNP_SUB_MBX_SIZE;
371
372 uint ctlr_port_num = 0; // FNPs are single ported
373 uint iom_unit_idx = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].iom_unit_idx;
374 uint chan_num = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].chan_num;
375
376 word36 data = 0;
377 l_putbits36_9 (& data, 9, 2); // cmd_data_len
378 l_putbits36_9 (& data, 18, 70); // op_code ack_echnego_init
379 l_putbits36_9 (& data, 27, 1); // io_cmd rcd
380 iom_direct_data_service (iom_unit_idx, chan_num, fsmbx+WORD2, & data, direct_store);
381
382 notifyCS (mbx, fnp_unit_idx, lineno);
383 }
384
fnp_rcd_ack_echnego_stop(uint mbx,int fnp_unit_idx,int lineno)385 static void fnp_rcd_ack_echnego_stop (uint mbx, int fnp_unit_idx, int lineno)
386 {
387 sim_debug (DBG_TRACE, & fnp_dev, "[%d]rcd ack_echnego_stop\n", lineno);
388 struct fnpUnitData_s * fudp = & fnpData.fnpUnitData [fnp_unit_idx];
389 word24 fsmbx = fudp->mailboxAddress + FNP_SUB_MBXES + mbx*FNP_SUB_MBX_SIZE;
390
391 uint ctlr_port_num = 0; // FNPs are single ported
392 uint iom_unit_idx = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].iom_unit_idx;
393 uint chan_num = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].chan_num;
394
395 word36 data = 0;
396 l_putbits36_9 (& data, 9, 2); // cmd_data_len
397 l_putbits36_9 (& data, 18, 71); // op_code ack_echnego_stop
398 l_putbits36_9 (& data, 27, 1); // io_cmd rcd
399 iom_direct_data_service (iom_unit_idx, chan_num, fsmbx+WORD2, & data, direct_store);
400
401 notifyCS (mbx, fnp_unit_idx, lineno);
402 }
403
fnp_rcd_line_disconnected(uint mbx,int fnp_unit_idx,int lineno)404 static void fnp_rcd_line_disconnected (uint mbx, int fnp_unit_idx, int lineno)
405 {
406 sim_debug (DBG_TRACE, & fnp_dev, "[%d]rcd line_disconnected\n", lineno);
407 struct fnpUnitData_s * fudp = & fnpData.fnpUnitData [fnp_unit_idx];
408 word24 fsmbx = fudp->mailboxAddress + FNP_SUB_MBXES + mbx*FNP_SUB_MBX_SIZE;
409
410 uint ctlr_port_num = 0; // FNPs are single ported
411 uint iom_unit_idx = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].iom_unit_idx;
412 uint chan_num = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].chan_num;
413
414 word36 data = 0;
415 l_putbits36_9 (& data, 9, 2); // cmd_data_len
416 l_putbits36_9 (& data, 18, 0101); // op_code cmd_data_len
417 l_putbits36_9 (& data, 27, 1); // io_cmd rcd
418 iom_direct_data_service (iom_unit_idx, chan_num, fsmbx+WORD2, & data, direct_store);
419
420 notifyCS (mbx, fnp_unit_idx, lineno);
421 }
422
fnp_rcd_input_in_mailbox(uint mbx,int fnp_unit_idx,int lineno)423 static void fnp_rcd_input_in_mailbox (uint mbx, int fnp_unit_idx, int lineno)
424 {
425 sim_debug (DBG_TRACE, & fnp_dev, "[%d]rcd input_in_mailbox\n", lineno);
426 struct fnpUnitData_s * fudp = & fnpData.fnpUnitData [fnp_unit_idx];
427 struct t_line * linep = & fudp->MState.line[lineno];
428 word24 fsmbx = fudp->mailboxAddress + FNP_SUB_MBXES + mbx*FNP_SUB_MBX_SIZE;
429
430 uint ctlr_port_num = 0; // FNPs are single ported
431 uint iom_unit_idx = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].iom_unit_idx;
432 uint chan_num = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].chan_num;
433
434 uint n_chars = min(linep->nPos, 100);
435
436 //Sim_printf ("fnp_rcd_input_in_mailbox nPos %d\n", linep->nPos);
437 word36 data = 0;
438 l_putbits36_9 (& data, 9, (word9) n_chars); // n_chars
439 l_putbits36_9 (& data, 18, 0102); // op_code input_in_mailbox
440 l_putbits36_9 (& data, 27, 1); // io_cmd rcd
441 iom_direct_data_service (iom_unit_idx, chan_num, fsmbx+WORD2, & data, direct_store);
442
443 // data goes in mystery [0..24]
444
445 //sim_printf ("short in; line %d tally %d\n", lineno, linep->nPos);
446 #if 0
447 if_sim_debug (DBG_TRACE, & fnp_dev) {
448 { sim_printf ("[[%d]FNP emulator: short IN: '", lineno);
449 for (int i = 0; i < linep->nPos; i ++)
450 {
451 if (isgraph (linep->buffer [i]))
452 sim_printf ("%c", linep->buffer [i]);
453 else
454 sim_printf ("\\%03o", linep->buffer [i]);
455 }
456 sim_printf ("']\n");
457 }
458 }
459 #endif
460 #if 0
461 { sim_printf ("IN: ");
462 for (int i = 0; i < n_chars; i ++)
463 //sim_printf ("%c", isgraph (linep->buffer [i]) ? linep->buffer [i] : '.');
464 if (isgraph (linep->buffer [i]))
465 sim_printf ("%c", linep->buffer [i]);
466 else
467 sim_printf ("\\%03o", linep->buffer [i]);
468
469 sim_printf ("\n");
470 }
471 #endif
472 uint j = 0;
473 for (uint i = 0; i < n_chars; i += 4, j++)
474 {
475 word36 v = 0;
476 if (i < linep->nPos)
477 l_putbits36_9 (& v, 0, linep->buffer [i]);
478 if (i + 1 < linep->nPos)
479 l_putbits36_9 (& v, 9, linep->buffer [i + 1]);
480 if (i + 2 < linep->nPos)
481 l_putbits36_9 (& v, 18, linep->buffer [i + 2]);
482 if (i + 3 < linep->nPos)
483 l_putbits36_9 (& v, 27, linep->buffer [i + 3]);
484 iom_direct_data_service (iom_unit_idx, chan_num, fsmbx+MYSTERY+j, & v, direct_store);
485 }
486
487 // command_data is at mystery[25]?
488
489 // temporary until the logic is in place XXX
490 int output_chain_present = 0;
491
492 data = 0;
493 l_putbits36_1 (& data, 16, (word1) output_chain_present);
494 l_putbits36_1 (& data, 17, linep->input_break ? 1 : 0);
495 iom_direct_data_service (iom_unit_idx, chan_num, fsmbx+INP_COMMAND_DATA, & data, direct_store);
496
497 #if 0
498 sim_printf (" %012"PRIo64"\n", smbxp -> word1);
499 sim_printf (" %012"PRIo64"\n", smbxp -> word2);
500 for (int i = 0; i < 26; i ++)
501 sim_printf (" %012"PRIo64"\n", smbxp -> mystery [i]);
502 sim_printf ("interrupting!\n");
503 #endif
504
505 notifyCS (mbx, fnp_unit_idx, lineno);
506 }
507
fnp_rcd_line_status(uint mbx,int fnp_unit_idx,int lineno)508 static void fnp_rcd_line_status (uint mbx, int fnp_unit_idx, int lineno)
509 {
510 struct fnpUnitData_s * fudp = & fnpData.fnpUnitData [fnp_unit_idx];
511 struct t_line * linep = & fudp->MState.line[lineno];
512 word24 fsmbx = fudp->mailboxAddress + FNP_SUB_MBXES + mbx*FNP_SUB_MBX_SIZE;
513
514 uint ctlr_port_num = 0; // FNPs are single ported
515 uint iom_unit_idx = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].iom_unit_idx;
516 uint chan_num = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].chan_num;
517
518 word36 data = 0;
519 l_putbits36_9 (& data, 9, 2); // cmd_data_len
520 l_putbits36_9 (& data, 18, 0124); // op_code line_status
521 l_putbits36_9 (& data, 27, 1); // io_cmd rcd
522 iom_direct_data_service (iom_unit_idx, chan_num, fsmbx+WORD2, & data, direct_store);
523
524 iom_direct_data_service (iom_unit_idx, chan_num, fsmbx+MYSTERY+0, & linep->lineStatus0, direct_store);
525 iom_direct_data_service (iom_unit_idx, chan_num, fsmbx+MYSTERY+1, & linep->lineStatus1, direct_store);
526
527 notifyCS (mbx, fnp_unit_idx, lineno);
528 }
529
fnp_rcd_accept_input(uint mbx,int fnp_unit_idx,int lineno)530 static void fnp_rcd_accept_input (uint mbx, int fnp_unit_idx, int lineno)
531 {
532 sim_debug (DBG_TRACE, & fnp_dev, "[%d]rcd accept_input\n", lineno);
533 struct fnpUnitData_s * fudp = & fnpData.fnpUnitData [fnp_unit_idx];
534 struct t_line * linep = & fudp->MState.line[lineno];
535 word24 fsmbx = fudp->mailboxAddress + FNP_SUB_MBXES + mbx*FNP_SUB_MBX_SIZE;
536
537 uint ctlr_port_num = 0; // FNPs are single ported
538 uint iom_unit_idx = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].iom_unit_idx;
539 uint chan_num = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].chan_num;
540
541 //sim_printf ("accept_input mbx %d fnp_unit_idx %d lineno %d nPos %d\n", mbx, fnp_unit_idx, lineno, linep->nPos);
542 word36 data = 0;
543 l_putbits36_18 (& data, 0, (word18) linep->nPos); // cmd_data_len XXX
544 l_putbits36_9 (& data, 18, 0112); // op_code accept_input
545 l_putbits36_9 (& data, 27, 1); // io_cmd rcd
546 iom_direct_data_service (iom_unit_idx, chan_num, fsmbx+WORD2, & data, direct_store);
547
548 // AN85 is just wrong. CS expects us to specify the number of buffers
549 // and sizes.
550
551 data = 1;
552 iom_direct_data_service (iom_unit_idx, chan_num, fsmbx+N_BUFFERS, & data, direct_store);
553 // DCW for buffer (1)
554 data = 0;
555 l_putbits36_12 (& data, 24, (word12) linep->nPos);
556 iom_direct_data_service (iom_unit_idx, chan_num, fsmbx+DCWS+0, & data, direct_store);
557
558 // Tell the MCS that we have emptied the output buffer; because of the
559 // miracle of TCP, this is essentially true. echnego cares about this
560 // for reason that are not clear to me.
561 word1 output_chain_present = 0;
562
563 data = 0;
564 l_putbits36_1 (& data, 16, (word1) output_chain_present);
565 l_putbits36_1 (& data, 17, linep->input_break ? 1 : 0);
566 iom_direct_data_service (iom_unit_idx, chan_num, fsmbx+INP_COMMAND_DATA, & data, direct_store);
567
568 fudp -> fnpMBXlineno [mbx] = lineno;
569 notifyCS (mbx, fnp_unit_idx, lineno);
570 }
571
fnp_rcd_line_break(uint mbx,int fnp_unit_idx,int lineno)572 static void fnp_rcd_line_break (uint mbx, int fnp_unit_idx, int lineno)
573 {
574 sim_debug (DBG_TRACE, & fnp_dev, "[%d]rcd line_break\n", lineno);
575 struct fnpUnitData_s * fudp = & fnpData.fnpUnitData [fnp_unit_idx];
576 word24 fsmbx = fudp->mailboxAddress + FNP_SUB_MBXES + mbx*FNP_SUB_MBX_SIZE;
577
578 uint ctlr_port_num = 0; // FNPs are single ported
579 uint iom_unit_idx = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].iom_unit_idx;
580 uint chan_num = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].chan_num;
581
582 word36 data = 0;
583 l_putbits36_9 (& data, 9, 0); // cmd_data_len XXX
584 l_putbits36_9 (& data, 18, 0113); // op_code line_break
585 l_putbits36_9 (& data, 27, 1); // io_cmd rcd
586 iom_direct_data_service (iom_unit_idx, chan_num, fsmbx+WORD2, & data, direct_store);
587
588 notifyCS (mbx, fnp_unit_idx, lineno);
589 }
590
fnp_rcd_send_output(uint mbx,int fnp_unit_idx,int lineno)591 static void fnp_rcd_send_output (uint mbx, int fnp_unit_idx, int lineno)
592 {
593 sim_debug (DBG_TRACE, & fnp_dev, "[%d]rcd send_output\n", lineno);
594 struct fnpUnitData_s * fudp = & fnpData.fnpUnitData [fnp_unit_idx];
595 word24 fsmbx = fudp->mailboxAddress + FNP_SUB_MBXES + mbx*FNP_SUB_MBX_SIZE;
596
597 uint ctlr_port_num = 0; // FNPs are single ported
598 uint iom_unit_idx = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].iom_unit_idx;
599 uint chan_num = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].chan_num;
600
601 word36 data = 0;
602 l_putbits36_9 (& data, 9, 0); // cmd_data_len XXX
603 l_putbits36_9 (& data, 18, 0105); // op_code send_output
604 l_putbits36_9 (& data, 27, 1); // io_cmd rcd
605 iom_direct_data_service (iom_unit_idx, chan_num, fsmbx+WORD2, & data, direct_store);
606
607 notifyCS (mbx, fnp_unit_idx, lineno);
608 }
609
fnp_rcd_acu_dial_failure(uint mbx,int fnp_unit_idx,int lineno)610 static void fnp_rcd_acu_dial_failure (uint mbx, int fnp_unit_idx, int lineno)
611 {
612 sim_debug (DBG_TRACE, & fnp_dev, "[%d]rcd acu_dial_failure\n", lineno);
613 //sim_printf ("acu_dial_failure %d %d %d\n", mbx, fnp_unit_idx, lineno);
614 struct fnpUnitData_s * fudp = & fnpData.fnpUnitData [fnp_unit_idx];
615 word24 fsmbx = fudp->mailboxAddress + FNP_SUB_MBXES + mbx*FNP_SUB_MBX_SIZE;
616
617 uint ctlr_port_num = 0; // FNPs are single ported
618 uint iom_unit_idx = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].iom_unit_idx;
619 uint chan_num = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].chan_num;
620
621 word36 data = 0;
622 l_putbits36_9 (& data, 9, 2); // cmd_data_len XXX
623 l_putbits36_9 (& data, 18, 82); // op_code acu_dial_failure
624 l_putbits36_9 (& data, 27, 1); // io_cmd rcd
625 iom_direct_data_service (iom_unit_idx, chan_num, fsmbx+WORD2, & data, direct_store);
626
627 notifyCS (mbx, fnp_unit_idx, lineno);
628 }
629
fnp_rcd_accept_new_terminal(uint mbx,int fnp_unit_idx,int lineno)630 static void fnp_rcd_accept_new_terminal (uint mbx, int fnp_unit_idx, int lineno)
631 {
632 sim_debug (DBG_TRACE, & fnp_dev, "[%d]rcd accept_new_terminal\n", lineno);
633 //sim_printf ("accept_new_terminal %d %d %d\n", mbx, fnp_unit_idx, lineno);
634 struct fnpUnitData_s * fudp = & fnpData.fnpUnitData [fnp_unit_idx];
635 struct t_line * linep = & fudp->MState.line[lineno];
636 word24 fsmbx = fudp->mailboxAddress + FNP_SUB_MBXES + mbx*FNP_SUB_MBX_SIZE;
637
638 uint ctlr_port_num = 0; // FNPs are single ported
639 uint iom_unit_idx = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].iom_unit_idx;
640 uint chan_num = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].chan_num;
641
642 word36 data = 0;
643 l_putbits36_9 (& data, 9, 2); // cmd_data_len XXX
644 l_putbits36_9 (& data, 18, 64); // op_code accept_new_terminal
645 l_putbits36_9 (& data, 27, 1); // io_cmd rcd
646 iom_direct_data_service (iom_unit_idx, chan_num, fsmbx+WORD2, & data, direct_store);
647
648 // pcb.line_type, dialup_info.line_type = bin (sub_mbx.command_data (1), 17);
649 // if sub_mbx.command_data (2)
650 // then pcb.baud_rate = baud_table (bin (sub_mbx.command_data (2), 17));
651
652 // declare (LINE_MC initial (-2),
653 // LINE_TELNET initial (-1),
654 // LINE_UNKNOWN initial (0),
655 // LINE_ASCII initial (1),
656 // LINE_1050 initial (2),
657 // LINE_2741 initial (3),
658 // LINE_ARDS initial (4),
659 // LINE_SYNCH initial (5),
660 // LINE_G115 initial (6),
661 // LINE_BSC initial (7),
662 // LINE_ETX initial (8),
663 // LINE_VIP initial (9),
664 // LINE_ASYNC1 initial (10),
665 // LINE_ASYNC2 initial (11),
666 // LINE_ASYNC3 initial (12),
667 // LINE_SYNC1 initial (13),
668 // LINE_SYNC2 initial (14),
669 // LINE_SYNC3 initial (15),
670 // LINE_POLLED_VIP initial (16),
671 // LINE_X25LAP initial (17),
672 // LINE_HDLC initial (18),
673 // LINE_COLTS initial (19),
674 // LINE_DSA initial (20),
675 // LINE_HASP_OPR initial (21)
676 // ) fixed bin internal static options (constant);
677
678 // dcl 1 dialup_info aligned, /* for use with DIALUP interrupt */
679 // 2 line_type fixed bin (9) unal uns,
680 // 2 buffer_pad fixed bin (9) unal uns, /* free space multiplexer would like in output bufs */
681 // 2 baud_rate fixed bin (18) unal uns,
682 // 2 max_buf_size fixed bin (9) unal uns,
683 // 2 receive_mode_device bit (1) unal, /* device must be told to enter receive mode */
684 // 2 pad bit (26) unal;
685
686 data = 0;
687 l_putbits36_9 (& data, 27, linep->lineType); // ??? 0 instead of 27 ?
688 iom_direct_data_service (iom_unit_idx, chan_num, fsmbx+MYSTERY+0, & data, direct_store);
689 data = 0;
690 iom_direct_data_service (iom_unit_idx, chan_num, fsmbx+MYSTERY+1, & data, direct_store);
691
692 notifyCS (mbx, fnp_unit_idx, lineno);
693 }
694
fnp_rcd_wru_timeout(uint mbx,int fnp_unit_idx,int lineno)695 static void fnp_rcd_wru_timeout (uint mbx, int fnp_unit_idx, int lineno)
696 {
697 sim_debug (DBG_TRACE, & fnp_dev, "[%d]rcd wru_timeout\n", lineno);
698 //sim_printf ("wru_timeout %d %d %d\n", mbx, fnp_unit_idx, lineno);
699 struct fnpUnitData_s * fudp = & fnpData.fnpUnitData [fnp_unit_idx];
700 word24 fsmbx = fudp->mailboxAddress + FNP_SUB_MBXES + mbx*FNP_SUB_MBX_SIZE;
701
702 uint ctlr_port_num = 0; // FNPs are single ported
703 uint iom_unit_idx = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].iom_unit_idx;
704 uint chan_num = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].chan_num;
705
706 word36 data = 0;
707 l_putbits36_9 (& data, 9, 2); // cmd_data_len XXX
708 l_putbits36_9 (& data, 18, 0114); // op_code wru_timeout
709 l_putbits36_9 (& data, 27, 1); // io_cmd rcd
710 iom_direct_data_service (iom_unit_idx, chan_num, fsmbx+WORD2, & data, direct_store);
711
712 notifyCS (mbx, fnp_unit_idx, lineno);
713 }
714
715 // Process an input character according to the line discipline.
716 // Return true if buffer should be shipped to the CS
717
processInputCharacter(struct t_line * linep,unsigned char kar,UNUSED bool endOfBuffer)718 static inline bool processInputCharacter (struct t_line * linep, unsigned char kar, UNUSED bool endOfBuffer)
719 {
720 if (! linep->line_client)
721 {
722 sim_warn ("processInputCharacter bad client\r\n");
723 return false;
724 }
725 #ifdef TUN
726 // TUN doesn't have a client
727 if (! linep->is_tun)
728 #endif
729 {
730 // telnet sends keyboard returns as CR/NUL. Drop the null when we see it;
731 uvClientData * p = linep->line_client->data;
732 //sim_printf ("kar %03o isTelnet %d was CR %d is Null %d\n", kar, !!p->telnetp, linep->was_CR, kar == 0);
733 //sim_printf ("%03o %c\n", kar, isgraph(kar)? kar : '#');
734 if (p && p->telnetp && linep->was_CR && kar == 0)
735 {
736 //sim_printf ("dropping nul\n");
737 linep->was_CR = false;
738 return false;
739 }
740 linep->was_CR = kar == 015;
741 //sim_printf ("was CR %d\n", linep->was_CR);
742 }
743
744 //sim_printf ("%03o %c\n", kar, isgraph (kar) ? kar : '.');
745 if (linep->service == service_login)
746 {
747 if (linep->echoPlex)
748 {
749 // echo \r, \n & \t
750
751 // echo a CR when a LF is typed
752 if (linep->crecho && kar == '\n')
753 {
754 fnpuv_start_writestr (linep->line_client, (unsigned char *) "\r\n");
755 }
756
757 // echo and inserts a LF in the users input stream when a CR is typed
758 else if (linep->lfecho && kar == '\r')
759 {
760 fnpuv_start_writestr (linep->line_client, (unsigned char *) "\r\n");
761 }
762
763 // echo the appropriate number of spaces when a TAB is typed
764 else if (linep->tabecho && kar == '\t')
765 {
766 // since nPos starts at 0 this'll work well with % operator
767 uint nCol = linep->nPos;
768 // for now we use tabstops of 1,11,21,31,41,51, etc...
769 nCol += 10; // 10 spaces/tab
770 int nSpaces = 10 - ((int) nCol % 10);
771 for(int i = 0 ; i < nSpaces ; i += 1)
772 fnpuv_start_writestr (linep->line_client, (unsigned char *) " ");
773 }
774
775 // XXX slightly bogus logic here..
776 // ^R ^U ^H DEL LF CR FF ETX
777 else if (kar == '\022' || kar == '\025' || kar == '\b' ||
778 kar == 127 || kar == '\n' || kar == '\r' ||
779 kar == '\f' || kar == '\003')
780 {
781 // handled below
782 }
783
784 // echo character
785 else
786 {
787 unsigned char str [2] = { kar, 0 };
788 fnpuv_start_writestr (linep->line_client, str);
789 }
790 } // if echoPlex
791
792 // send of each and every character
793 if (linep->breakAll)
794 {
795 linep->buffer[linep->nPos ++] = kar;
796 linep->buffer[linep->nPos] = 0;
797 // Echnego
798
799 // MTB-418, pg 13.
800 // "If the [input_break] bit is on, the delivery consists.of characters none of
801 // which were echoed by the multiplexer; the multiplexer may decide for any
802 // reason (e.g., internal buffer shortages, internal races, etc.) to stop
803 // echoing (as can ring zero vis-a-vis ring 4). Of course, it must stop echoing
804 // for the defined echo negotiation.break conditions. If the "break character"
805 // bit is off, the delivery consists of characters all of which were echoed by
806 // the multiplexer, except for perhaps the la~ character of the delivery. MCS
807 // must determine, for such a delivery, whether the last character of such a
808 // delivery was capable of being echoed by the multiplexer, and if so, assume
809 // that it was, otherwise not.
810
811 // "start negotiated echo'', via a control order, also specifying the number of
812 // characters left on the line
813
814 // The multiplexer input processor will also count characters processed by it
815 // since it last echoed a character.
816
817 #ifdef ECHNEGO_DEBUG
818 sim_printf ("\nkar <%c>\n", isprint (kar) ? kar : '*');
819 #endif
820 // Are we echoing?
821 if (linep->echnego_on)
822 {
823 if (linep->echnego_break_table[kar])
824 {
825 // Break.
826 #ifdef ECHNEGO_DEBUG
827 sim_printf ("break\n");
828 #endif
829 // MTB418 pg 14:
830 // "Whenever the multiplexer delivers to the Ring Zero MCS
831 // interrupt side a character that takes ring zero out of
832 // the echo state, the multiplexer itself will be known to
833 // have stopped echoing."
834
835 // Leave echnego mode.
836 linep->echnego_on = false;
837
838 // If the multiplexer delivers up a non-empty shipment of characters
839 // without the break character bit on, and the specific multiplexer has
840 // been found to be knowledgeable about multiplexer echo negotiation
841 // (i.e., earlier accepted the ''start negotiated echo" control order) all
842 // characters except the last in the shipment are known to have been
843 // echoed by the multiplexer, which has apparently honored the order call
844 // which was issued at the time ring zero went into the echoing state.
845 // Thus, this only possible if ring zero is in the echoing state. Those
846 // characters are counted by ring zero as "having been echoed by ring
847 // zero" (as far is ring four is concerned) and are not echoed by ring
848 // zero. The last character is checked for stopping ring-zero echo, and
849 // if it would not stop ring zero echo, is treated as one of the
850 // multiplexer-echoed characters. If it would stop ring zero echo, it is
851 // processed as today, indeed takes ring zero out of the echo state,
852 // and causes ring 4 to be woken up, as today.
853
854 // "If [input_break] is off, the delivery consists
855 // of characters all of which were echoed ...,
856 // except for perhaps the last ... ."
857 linep->input_break = false;
858
859 // MTB418 pg 15:
860 // "This determination is made by the ''input processor''
861 // of the multiplexer based upon a value called the
862 // synchronization counter sent with the start negotiated
863 // echo control order: the value sent by ring zero is the
864 // count of all characters received by the ring zero
865 // interrupt side since the last character echoed by the
866 // multiplexer."
867 linep->echnego_unechoed_cnt ++;
868 #ifdef ECHNEGO_DEBUG
869 sim_printf ("echnego break nPos %d unechoed cnt %d\r\n",
870 linep->nPos, linep->echnego_unechoed_cnt);
871 #endif
872 linep->accept_input = 1;
873 return true;
874 } // if break char
875
876 #ifdef ECHNEGO_DEBUG
877 sim_printf ("echoing '%c'\r\n", kar);
878 #endif
879 // Not break; so echo
880 unsigned char str [2] = { kar, 0 };
881 fnpuv_start_writestr (linep->line_client, str);
882
883 // MTB418 pg 15:
884 // "This determination is made by the ''input processor'' of
885 // the multiplexer based upon a value called the
886 // synchronization counter sent with the start negotiated echo
887 // control order: the value sent by ring zero is the count of
888 // all characters received by the ring zero interrupt side
889 // since the last character echoed by the multiplexer."
890 linep->echnego_unechoed_cnt = 0;
891
892 if (linep->echnego_screen_left)
893 linep->echnego_screen_left --;
894 #ifdef ECHNEGO_DEBUG
895 sim_printf ("echnego_screen_left %u\n", linep->echnego_screen_left);
896 #endif
897
898 if (linep->echnego_screen_left == 0)
899 {
900 // End of line.
901 #ifdef ECHNEGO_DEBUG
902 sim_printf ("end of line\n");
903 #endif
904 // MTB418 pg 14:
905 // "Whenever the multiplexer delivers to the Ring Zero MCS
906 // interrupt side a character that takes ring zero out of
907 // the echo state, the multiplexer itself will be known to
908 // have stopped echoing."
909
910 // Leave echnego mode.
911 linep->echnego_on = false;
912
913 // If the multiplexer delivers up a shipment of characters with the
914 // "break character" bit on, either the request was not honored, or the
915 // specific multiplexer does not support echo negotiation, the multiplexer
916 // decided randomly (i.e., for internal reasons) to stop or not start
917 // echoing, or the first character in the delivery is a break character· or
918 // exceeds the length of screen left; in any case, the delivery is
919 // entirely of non-echoed characters. These cases are indistinguishable
920 // from each other and from the only case today when in the ring zero echo
921 // state, and handled identically as today. The delivery is scanned,
922 // a possible leading prefix of echoable characters echoed by ring zero,
923 // and the characters made available (if ring zero leaves the ring zero
924 // echo state while processing them) to ring 4, which would then be woken
925 // up.
926
927 linep->input_break = false;
928
929
930 #if 0
931 // MTB418 pg 15:
932 // "This determination is made by the ''input processor''
933 // of the multiplexer based upon a value called the
934 // synchronization counter sent with the start negotiated
935 // echo control order: the value sent by ring zero is the
936 // count of all characters received by the ring zero
937 // interrupt side since the last character echoed by the
938 // multiplexer."
939 linep->echnego_unechoed_cnt ++;
940 #endif
941 #ifdef ECHNEGO_DEBUG
942 sim_printf ("echnego end of line nPos %d unechoed cnt %d\r\n",
943 linep->nPos, linep->echnego_unechoed_cnt);
944 #endif
945 linep->accept_input = 1;
946 return true;
947 }
948
949 return true;
950 } // echo nego
951
952 // MTB418 pg 15:
953 // "This determination is made by the ''input processor''
954 // of the multiplexer based upon a value called the
955 // synchronization counter sent with the start negotiated
956 // echo control order: the value sent by ring zero is the
957 // count of all characters received by the ring zero
958 // interrupt side since the last character echoed by the
959 // multiplexer."
960 linep->echnego_unechoed_cnt += linep->nPos;
961
962 linep->input_break = true;
963 linep->accept_input = 1;
964 #ifdef ECHNEGO_DEBUG
965 sim_printf ("break nPos %d unechoed cnt %d\r\n",
966 linep->nPos, linep->echnego_unechoed_cnt);
967 #endif
968 return true;
969 } // break all
970
971 if ((linep-> frame_begin != 0 &&
972 linep-> frame_begin == kar) ||
973 (linep-> frame_end != 0 &&
974 linep-> frame_end == kar))
975 {
976 #if 0
977 // Framing chars are dropped. Is that right?.
978 if (linep->nPos != 0)
979 {
980 linep->accept_input = 1;
981 linep->input_break = true;
982 return true;
983 }
984 // Frame character on an empty frame; keep going.
985 return false;
986 #else
987 // XXX This code assumes that only 'frame_end' is in play, as in Kermit behavior
988 linep->buffer[linep->nPos++] = kar;
989 // Pad to frame size with nulls
990 uint frsz = linep->block_xfer_in_frame_sz;
991 while ((size_t) linep->nPos < sizeof (linep->buffer) && linep->nPos < frsz)
992 linep->buffer[linep->nPos++] = 0;
993 linep->accept_input = 1;
994 linep->input_break = true;
995 return true;
996 #endif
997 }
998
999 // Multics seems to want CR changed to LF
1000 if (kar == '\r')
1001 kar = '\n';
1002
1003 switch (kar)
1004 {
1005 case '\n': // NL
1006 case '\r': // CR
1007 case '\f': // FF
1008 {
1009 kar = '\n'; // translate to NL
1010 linep->buffer[linep->nPos++] = kar;
1011 linep->buffer[linep->nPos] = 0;
1012 linep->accept_input = 1;
1013 linep->input_break = true;
1014 //sim_printf ("processInputCharacter sees NL; sets input_break\n");
1015 return true;
1016 }
1017
1018 case 0x03: // ETX (^C) // line break
1019 {
1020 if (linep->handleQuit)
1021 {
1022 linep->line_break=true;
1023 // Treating line break as out of band, but pausing
1024 // buffer processing. Not sure this makes any difference
1025 // as the processing will resume on the next processing loop
1026 return true;
1027 }
1028 }
1029 break;
1030
1031 case '\b': // backspace
1032 case 127: // delete
1033 {
1034 if (linep->nPos > 0)
1035 {
1036 fnpuv_start_writestr (linep->line_client, (unsigned char *) "\b \b"); // remove char from line
1037 linep->nPos -= 1; // back up buffer pointer
1038 linep->buffer[linep->nPos] = 0; // remove char from buffer
1039 }
1040 else
1041 {
1042 // remove char from line
1043 fnpuv_start_writestr (linep->line_client, (unsigned char *) "\a");
1044 }
1045 return false;
1046 }
1047
1048 case 21: // ^U kill
1049 {
1050 linep->nPos = 0;
1051 linep->buffer[linep->nPos] = 0;
1052 fnpuv_start_writestr (linep->line_client, (unsigned char *) "^U\r\n");
1053 return false;
1054 }
1055
1056 case 0x12: // ^R
1057 {
1058 fnpuv_start_writestr (linep->line_client, (unsigned char *) "^R\r\n"); // echo ^R
1059 fnpuv_start_writestr (linep->line_client, linep->buffer);
1060 return false;
1061 }
1062
1063 default:
1064 break;
1065 }
1066
1067 }
1068
1069 // Just a character in cooked mode; append it to the buffer
1070 linep->buffer[linep->nPos++] = kar;
1071 linep->buffer[linep->nPos] = 0;
1072
1073 // If we filled the buffer, move it along
1074
1075 if (
1076 // Dial out or slave and inBuffer exhausted
1077 ((linep->service == service_autocall || linep->service == service_slave) && linep->inUsed >= linep->inSize) ||
1078
1079 // Internal buffer full
1080 (size_t) linep->nPos >= sizeof (linep->buffer) ||
1081
1082 #if 0
1083 // block xfer buffer size met
1084 (linep->block_xfer_out_frame_sz != 0 && linep->nPos >= linep->block_xfer_out_frame_sz) ||
1085
1086 // 'listen' command buffer size met
1087 (linep->inputBufferSize != 0 && linep->nPos >= (int) linep->inputBufferSize))
1088 #endif
1089 ((linep->block_xfer_out_frame_sz != 0)
1090 ?
1091 // block xfer buffer size met
1092 (linep->nPos >= linep->block_xfer_out_frame_sz)
1093 :
1094 // 'listen' command buffer size met
1095 (linep->inputBufferSize != 0 && linep->nPos >= linep->inputBufferSize))
1096 )
1097 {
1098 linep->accept_input = 1;
1099 linep->input_break = false;
1100 // To make IMFT work...
1101 if (linep->service == service_slave || linep->service == service_autocall)
1102 {
1103 #ifdef TUN
1104 if (linep->is_tun)
1105 linep->input_break = endOfBuffer;
1106 else
1107 #endif
1108 linep->input_break = true;
1109 }
1110
1111 return true;
1112 }
1113 return false;
1114 }
1115
1116 // The 3270 controller received a EOR
1117
fnpRecvEOR(uv_tcp_t * client)1118 void fnpRecvEOR (uv_tcp_t * client)
1119 {
1120 if (! client || ! client->data)
1121 {
1122 sim_warn ("fnpRecvEOR bad client data\r\n");
1123 return;
1124 }
1125 uvClientData * p = client->data;
1126 fnpData.ibm3270ctlr[ASSUME0].stations[p->stationNo].EORReceived = true;
1127 fnpData.ibm3270ctlr[ASSUME0].stations[p->stationNo].hdr_sent = false;
1128 }
1129
fnpProcessBuffer(struct t_line * linep)1130 static void fnpProcessBuffer (struct t_line * linep)
1131 {
1132 // The connection could have closed when we were not looking
1133 #ifdef TUN
1134 if ((! linep->is_tun) && ! linep->line_client)
1135 #else
1136 if (! linep->line_client)
1137 #endif
1138 {
1139 if (linep->inBuffer)
1140 free (linep->inBuffer);
1141 linep->inBuffer = NULL;
1142 linep->inSize = 0;
1143 linep->inUsed = 0;
1144 return;
1145 }
1146
1147 while (linep->inBuffer && linep->inUsed < linep->inSize)
1148 {
1149 unsigned char c = linep->inBuffer [linep->inUsed ++];
1150 //sim_printf ("processing %d/%d %o '%c'\n", linep->inUsed-1, linep->inSize, c, isprint (c) ? c : '?');
1151 bool eob = linep->inUsed >= linep->inSize;
1152 if (eob)
1153 {
1154 free (linep->inBuffer);
1155 linep->inBuffer = NULL;
1156 linep->inSize = 0;
1157 linep->inUsed = 0;
1158 // The connection could have been closed when we weren't looking
1159 if (linep->line_client)
1160 fnpuv_read_start (linep->line_client);
1161 }
1162 if (linep->service == service_3270)
1163 {
1164 linep->buffer[linep->nPos++] = c;
1165 linep->buffer[linep->nPos] = 0;
1166 continue;
1167 }
1168 if (processInputCharacter (linep, c, eob))
1169 break;
1170 }
1171 }
1172
fnpProcessBuffers(void)1173 static void fnpProcessBuffers (void)
1174 {
1175 uint numunits = (uint) fnp_dev.numunits;
1176 for (uint fnp_unit_idx = 0; fnp_unit_idx < numunits; fnp_unit_idx ++)
1177 {
1178 if (! fnpData.fnpUnitData[fnp_unit_idx].fnpIsRunning)
1179 continue;
1180 for (uint lineno = 0; lineno < MAX_LINES; lineno ++)
1181 {
1182 struct t_line * linep = & fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno];
1183
1184 // If an accept_input request is posted, then buffer is busy.
1185 if (linep->accept_input)
1186 continue;
1187
1188 // If a input command ack is pending, then buffer is busy.
1189 if (linep->input_reply_pending)
1190 continue;
1191
1192 // If no data to process
1193 if (!linep->inBuffer)
1194 continue;
1195
1196 fnpProcessBuffer (linep);
1197 }
1198 }
1199 }
1200
1201 // dcl 1 line_stat aligned,
1202 // 2 op fixed binary (17) unaligned, /* contains reason for status */
1203 // 2 val (3) fixed binary (17) unaligned;
1204
1205
1206 // /* Values for line_stat.op */
1207 //
1208 // dcl (BID_FAILED initial (1),
1209 // BAD_BLOCK initial (2),
1210 // REVERSE_INTERRUPT initial (3),
1211 // TOO_MANY_NAKS initial (4),
1212 // FNP_WRITE_STATUS initial (5),
1213 // IBM3270_WRITE_COMPLETE initial (6),
1214 // IBM3270_WACK_MESSAGE initial (7),
1215 // IBM3270_WRITE_EOT initial (8),
1216 // IBM3270_WRITE_ABORT initial (9),
1217 // IBM3270_SELECT_FAILED initial (10),
1218 // IBM3270_WACK_SELECT initial (11),
1219 // IBM3270_NAK_OUTPUT initial (12),
1220 // HASP_INIT_COMPLETE initial (13),
1221 // HASP_FOREIGN_SWAB_RESET initial (14))
1222 // fixed binary static options (constant);
1223
1224 // Send a message to Multics
1225
set_3270_write_complete(UNUSED uv_tcp_t * client)1226 void set_3270_write_complete (UNUSED uv_tcp_t * client)
1227 {
1228 //uvClientData * p = client->data;
1229 //sim_printf ("set_3270_write_complete %p stn_no %d\r\n", p, p->stationNo);
1230 sim_debug (DBG_TRACE, & fnp_dev, "set_3270_write_complete\n");
1231 //fnpData.ibm3270ctlr[ASSUME0].stations[p->stationNo].write_complete = true;
1232 fnpData.ibm3270ctlr[ASSUME0].write_complete = true;
1233 }
1234
send_3270_msg(uint ctlr_no,unsigned char * msg,size_t len,bool brk)1235 static void send_3270_msg (uint ctlr_no, unsigned char * msg, size_t len, bool brk)
1236 {
1237 #if 0
1238 sim_printf ("send_3270_msg:");
1239 for (size_t i = 0; i < len; i ++) sim_printf (" %02x", msg[i]);
1240 sim_printf ("\r\n");
1241 for (size_t i = 0; i < len; i ++) sim_printf (" %03o", msg[i]);
1242 sim_printf ("\r\n");
1243 #endif
1244
1245 uint fnpno = fnpData.ibm3270ctlr[ctlr_no].fnpno;
1246 uint lineno = fnpData.ibm3270ctlr[ctlr_no].lineno;
1247 struct t_line * linep = & fnpData.fnpUnitData[fnpno].MState.line[lineno];
1248 if ((unsigned long) linep->nPos + len > sizeof (linep->buffer))
1249 sim_warn ("send_3270_msg overfull linep->buffer; dropping data\r\n");
1250 else
1251 {
1252 memcpy (linep->buffer + linep->nPos, msg, len);
1253 linep->nPos += len;
1254 }
1255 #if 0
1256 sim_printf ("send_3270_msg:");
1257 for (size_t i = 0; i < linep->nPos; i ++) sim_printf (" %02x", linep->buffer[i]);
1258 sim_printf ("\r\n");
1259 for (size_t i = 0; i < linep->nPos; i ++) sim_printf (" %03o", linep->buffer[i]);
1260 sim_printf ("\r\n");
1261 #endif
1262 linep->force_accept_input = true;
1263 linep->accept_input = 1;
1264 linep->input_break = brk ? 1 : 0;
1265 }
1266
1267 const unsigned char addr_map [ADDR_MAP_ENTRIES] =
1268 {
1269 0x40, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
1270 0xc8, 0xc9, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
1271 0x50, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
1272 0xd8, 0xd9, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f
1273 };
1274
send_stn_in_buffer(void)1275 static void send_stn_in_buffer (void)
1276 {
1277 sim_debug (DBG_TRACE, & fnp_dev, "fnp2 send_stn_in_buffer\r\n");
1278
1279 //dcl 1 text_msg unal based (textp), /* Format of normal text start */
1280 // 2 stx char (1),
1281 // 2 controller_address char (1),
1282 // 2 device_address char (1),
1283 // 2 aid char (1), /* Reason for input (which key) */
1284 // 2 cursor1 char (1),
1285 // 2 cursor2 char (1);
1286
1287 // ibm3270_mpx expects: STX text_msg ETX
1288 //
1289 // x3270 sends aid, cursor1, cursor2
1290
1291
1292 //sim_printf ("sending rcvd data\r\n");
1293
1294 uint fnpno = fnpData.ibm3270ctlr[ASSUME0].fnpno;
1295 uint lineno = fnpData.ibm3270ctlr[ASSUME0].lineno;
1296 struct t_line * linep = & fnpData.fnpUnitData[fnpno].MState.line[lineno];
1297
1298 // Idle until buffer availible
1299 if (linep->accept_input)
1300 return;
1301 if (linep->input_reply_pending)
1302 return;
1303
1304 struct ibm3270ctlr_s * ctlrp = & fnpData.ibm3270ctlr[ASSUME0];
1305 struct station_s * stnp = & fnpData.ibm3270ctlr[ASSUME0].stations[ctlrp->stn_no];
1306
1307 uint left = linep->sync_msg_size;
1308
1309 unsigned char * bufp = linep->buffer;
1310
1311 * bufp ++ = 0x2; // STX
1312 left --;
1313
1314 if (! stnp->hdr_sent)
1315 {
1316 * bufp ++ = addr_map [ASSUME0]; // Controller address
1317 left --;
1318 * bufp ++ = addr_map [ctlrp->stn_no]; // Station address
1319 left --;
1320 stnp->hdr_sent = true;
1321 }
1322
1323 uint n_to_send = stnp->stn_in_size - stnp->stn_in_used;
1324 if (n_to_send > left)
1325 n_to_send = left;
1326 if (n_to_send)
1327 {
1328 sim_debug (DBG_TRACE, & fnp_dev, "handling in used %u %u\r\n", stnp->stn_in_used, n_to_send);
1329 //send_3270_msg (ASSUME0, stnp->stn_in_buffer + stnp->stn_in_used, n_to_send, false);
1330 //return;
1331 memcpy (bufp, stnp->stn_in_buffer + stnp->stn_in_used, n_to_send);
1332 bufp += n_to_send;
1333 stnp->stn_in_used += n_to_send;
1334 left -= n_to_send;
1335 }
1336
1337 if (stnp->stn_in_used >= stnp->stn_in_size && left)
1338 {
1339 * bufp ++ = 0x3; // ETX
1340 left --;
1341
1342 free (stnp->stn_in_buffer);
1343 stnp->stn_in_buffer = NULL;
1344 stnp->stn_in_size = 0;
1345 stnp->stn_in_used = 0;
1346
1347 linep->input_break = 1;
1348 fnpData.ibm3270ctlr[ASSUME0].sending_stn_in_buffer = false;
1349 //unsigned char ETX = 0x3;
1350 //send_3270_msg (ASSUME0, & ETX, sizeof (ETX), true);
1351 }
1352 uint sz = (uint) (bufp - linep->buffer);
1353 if (sz)
1354 {
1355 linep->force_accept_input = true;
1356 linep->accept_input = 1;
1357 linep->nPos = sz;
1358 }
1359 else
1360 {
1361 //ctlrp->sending_stn_in_buffer = true;
1362 }
1363 }
1364
fnp_process_3270_event(void)1365 static void fnp_process_3270_event (void)
1366 {
1367 uint fnpno = fnpData.ibm3270ctlr[ASSUME0].fnpno;
1368 uint lineno = fnpData.ibm3270ctlr[ASSUME0].lineno;
1369 struct t_line * linep = & fnpData.fnpUnitData[fnpno].MState.line[lineno];
1370
1371 // Non-polling events
1372
1373 if (fnpData.ibm3270ctlr[ASSUME0].sending_stn_in_buffer)
1374 {
1375 send_stn_in_buffer ();
1376 return;
1377 }
1378
1379 if (fnpData.ibm3270ctlr[ASSUME0].write_complete)
1380 {
1381 fnpData.ibm3270ctlr[ASSUME0].write_complete = false;
1382 linep->lineStatus0 = 6llu << 18; // IBM3270_WRITE_COMPLETE
1383 linep->lineStatus1 = 0;
1384 linep->sendLineStatus = true;
1385 }
1386
1387 // Polling events
1388
1389 if (! fnpData.du3270_poll)
1390 return;
1391 fnpData.du3270_poll --;
1392 if (fnpData.du3270_poll)
1393 return;
1394 struct ibm3270ctlr_s * ctlrp = & fnpData.ibm3270ctlr[ASSUME0];
1395
1396 sim_debug (DBG_TRACE, & fnp_dev, "fnp2 3270 poll\n");
1397 //uint fnpno = fnpData.ibm3270ctlr[ASSUME0].fnpno;
1398 //uint lineno = fnpData.ibm3270ctlr[ASSUME0].lineno;
1399 //struct t_line * linep = & fnpData.fnpUnitData[fnpno].MState.line[lineno];
1400 //linep->lineStatus0 = 0;
1401 //linep->lineStatus1 = 0;
1402 if (ctlrp->pollDevChar == 127) // General poll
1403 {
1404 uint stn_cnt;
1405 for (stn_cnt = 0; stn_cnt < IBM3270_STATIONS_MAX; stn_cnt ++)
1406 {
1407 ctlrp->stn_no = (ctlrp->stn_no + 1) % IBM3270_STATIONS_MAX;
1408 struct station_s * stnp = & fnpData.ibm3270ctlr[ASSUME0].stations[ctlrp->stn_no];
1409 if (! stnp->client)
1410 continue;
1411 if (stnp->EORReceived)
1412 {
1413 stnp->EORReceived = false;
1414 ctlrp->sending_stn_in_buffer = true;
1415 fnpuv3270Poll (false);
1416 break;
1417 }
1418 }
1419 if (stn_cnt >= IBM3270_STATIONS_MAX)
1420 {
1421 // No response to poll; send EOT, stop polling
1422
1423 unsigned char EOT = 0x37;
1424 send_3270_msg (ASSUME0, & EOT, 1, true);
1425 fnpuv3270Poll (false);
1426 }
1427 }
1428 else
1429 {
1430 // Specific poll
1431 sim_debug (DBG_TRACE, & fnp_dev, "fnp2 specific poll\n");
1432 }
1433 }
1434
1435 //
1436 // Called @ 100Hz to process FNP background events
1437 //
1438
fnpProcessEvent(void)1439 void fnpProcessEvent (void)
1440 {
1441 // Run the libuv event loop once.
1442 // Handles tcp connections, drops, read data, write data done.
1443 fnpuvProcessEvent ();
1444
1445 // Move characters from inBuffer to buffer, based on line discipline
1446 // and data availibility
1447
1448 fnpProcessBuffers ();
1449
1450 // Look for posted requests
1451 uint numunits = (uint) fnp_dev.numunits;
1452 for (uint fnp_unit_idx = 0; fnp_unit_idx < numunits; fnp_unit_idx ++)
1453 {
1454 if (! fnpData.fnpUnitData[fnp_unit_idx].fnpIsRunning)
1455 continue;
1456 int mbx = findMbx (fnp_unit_idx);
1457 if (mbx == -1)
1458 continue;
1459 bool need_intr = false;
1460 for (int lineno = 0; lineno < MAX_LINES; lineno ++)
1461 {
1462 struct t_line * linep = & fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno];
1463
1464 #ifdef DISC_DELAY
1465 // Disconnect pending?
1466 if (linep -> line_disconnected > 1)
1467 {
1468 // Buffer not empty?
1469 if (linep->inBuffer && linep->inUsed < linep->inSize)
1470 {
1471 // Reset timer
1472 linep -> line_disconnected = DISC_DELAY;
1473 }
1474 else
1475 {
1476 // Decrement timer
1477 -- linep -> line_disconnected;
1478 }
1479 }
1480 #endif
1481
1482 // Are we waiting for the previous command to complete?
1483 if (linep->waitForMbxDone)
1484 continue;
1485
1486 // Need to send a 'send_output' command to CS?
1487
1488 bool do_send_output = linep->send_output == 1;
1489
1490 if (linep -> send_output > 0)
1491 linep->send_output --;
1492
1493 if (do_send_output)
1494 {
1495 fnp_rcd_send_output ((uint)mbx, (int) fnp_unit_idx, lineno);
1496 need_intr = true;
1497 }
1498
1499 // Need to send an 'acu_dial_failure' command to CS?
1500
1501 else if (linep->acu_dial_failure)
1502 {
1503 fnp_rcd_acu_dial_failure ((uint)mbx, (int) fnp_unit_idx, lineno);
1504 linep->acu_dial_failure = false;
1505 need_intr = true;
1506 }
1507
1508 // Need to send an 'accept_new_terminal' command to CS?
1509
1510 // linep->listen is check here for the case of a connection that was
1511 // made before Multics was booted to the point of setting listen.
1512 // If the accept_new_terminal call is made then, Multics rejects
1513 // the connection and sends a disconnect order. By checking 'listen',
1514 // the accept requests hangs around until Multics is ready.
1515
1516 else if (linep->listen && linep->accept_new_terminal)
1517 {
1518 fnp_rcd_accept_new_terminal ((uint)mbx, (int) fnp_unit_idx, lineno);
1519 linep->accept_new_terminal = false;
1520 need_intr = true;
1521 }
1522
1523 // Need to send an 'ack_echnego_init' command to CS?
1524
1525 else if (linep -> ack_echnego_init)
1526 {
1527 fnp_rcd_ack_echnego_init ((uint)mbx, (int) fnp_unit_idx, lineno);
1528 linep -> ack_echnego_init = false;
1529 linep -> send_output = SEND_OUTPUT_DELAY;
1530 need_intr = true;
1531 }
1532
1533 // Need to send an 'ack_echnego_stop' command to CS?
1534
1535 else if (linep -> ack_echnego_stop)
1536 {
1537 fnp_rcd_ack_echnego_stop ((uint)mbx, (int) fnp_unit_idx, lineno);
1538 linep -> ack_echnego_stop = false;
1539 linep -> send_output = SEND_OUTPUT_DELAY;
1540 need_intr = true;
1541 }
1542
1543 // Need to send an 'line_disconnected' command to CS?
1544
1545 #ifdef DISC_DELAY
1546 else if (linep -> line_disconnected == 1)
1547 {
1548 fnp_rcd_line_disconnected ((uint)mbx, (int) fnp_unit_idx, lineno);
1549 linep -> line_disconnected = 0;
1550 linep -> listen = false;
1551 need_intr = true;
1552 }
1553 #else
1554 else if (linep -> line_disconnected)
1555 {
1556 fnp_rcd_line_disconnected ((uint)mbx, (int) fnp_unit_idx, lineno);
1557 linep -> line_disconnected = false;
1558 linep -> listen = false;
1559 need_intr = true;
1560 }
1561 #endif
1562
1563 // Need to send an 'wru_timeout' command to CS?
1564
1565 else if (linep -> wru_timeout)
1566 {
1567 fnp_rcd_wru_timeout ((uint)mbx, (int) fnp_unit_idx, lineno);
1568 linep -> wru_timeout = false;
1569 need_intr = true;
1570 }
1571
1572 // Need to send an 'accept_input' or 'input_in_mailbox' command to CS?
1573
1574 else if (linep->accept_input && ! linep->waitForMbxDone)
1575 {
1576 if (linep->accept_input == 1)
1577 {
1578 // This check was added as part of 3270 support,
1579 // but breaks break key logic. Disabling until
1580 // a case can be made that the 3270 requires this.
1581 if (0 && linep->nPos == 0)
1582 {
1583 sim_printf ("dropping nPos of 0");
1584 }
1585 else
1586 {
1587 //sim_printf ("\n nPos %d\n", linep->nPos);
1588 #if 0
1589 {
1590 sim_printf ("\n nPos %d:", linep->nPos);
1591 for (int i = 0; i < linep->nPos; i ++)
1592 if (isgraph (linep->buffer [i]))
1593 sim_printf ("%c", linep->buffer [i]);
1594 else
1595 sim_printf ("\\%03o", linep->buffer [i]);
1596 //for (unsigned char * p = linep->buffer; *p; p ++)
1597 //if (isgraph (*p))
1598 //sim_printf ("%c", *p);
1599 //else
1600 //sim_printf ("\\%03o", *p);
1601 sim_printf ("\r\n");
1602 }
1603 #endif
1604 // There is a bufferfull of data that needs to be sent to the CS.
1605 // If the buffer has < 101 characters, use the 'input_in_mailbox'
1606 // command; otherwise use the 'accept_input/input_accepted'
1607 // sequence.
1608
1609 #if 0
1610 fnp_rcd_accept_input (mbx, (int) fnp_unit_idx, lineno);
1611 //linep->input_break = false;
1612 linep->input_reply_pending = true;
1613 // accept_input cleared below
1614 #else
1615 if (linep->force_accept_input || linep->nPos > 100)
1616 {
1617 fnp_rcd_accept_input ((uint)mbx, (int) fnp_unit_idx, lineno);
1618 //linep->input_break = false;
1619 linep->input_reply_pending = true;
1620 // accept_input cleared below
1621 need_intr = true;
1622 }
1623 else
1624 {
1625 fnp_rcd_input_in_mailbox ((uint)mbx, (int) fnp_unit_idx, lineno);
1626 sim_debug (DBG_TRACE, & fnp_dev, "FNP input_in_mailbox\n");
1627 linep->nPos = 0;
1628 // accept_input cleared below
1629 need_intr = true;
1630 }
1631 #endif
1632 }
1633 }
1634 linep->accept_input --;
1635 } // accept_input
1636
1637 // Need to send a 'line_break' command to CS?
1638 // This goes after the accept_input to that when BREAK occurs,
1639 // first the input is flushed and then the break signal is sent.
1640
1641 else if (linep->line_break)
1642 {
1643 fnp_rcd_line_break ((uint)mbx, (int) fnp_unit_idx, lineno);
1644 linep -> line_break = false;
1645 need_intr = true;
1646 linep -> send_output = SEND_OUTPUT_DELAY;
1647 }
1648
1649 else if (linep->sendLineStatus)
1650 {
1651 linep->sendLineStatus = false;
1652 fnp_rcd_line_status ((uint)mbx, (int) fnp_unit_idx, lineno);
1653 need_intr = true;
1654 }
1655
1656 else
1657 {
1658 continue;
1659 }
1660
1661 // One of the request processes may have consumed the
1662 // mailbox; make sure one is still available
1663
1664 mbx = findMbx (fnp_unit_idx);
1665 if (mbx == -1)
1666 break;
1667 } // for lineno
1668
1669 // If any of the mailboxes had a command posted.
1670 if (need_intr)
1671 {
1672 uint ctlr_port_num = 0; // FNPs are single ported
1673 uint iom_unit_idx = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].iom_unit_idx;
1674 uint chan_num = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].chan_num;
1675 send_general_interrupt (iom_unit_idx, chan_num, imwTerminatePic);
1676 }
1677 } // for fnp_unit_idx
1678
1679 #ifdef TUN
1680 fnpTUNProcessEvent ();
1681 #endif
1682 fnp_process_3270_event ();
1683 }
1684
fnpShowNUnits(UNUSED FILE * st,UNUSED UNIT * uptr,UNUSED int val,UNUSED const void * desc)1685 static t_stat fnpShowNUnits (UNUSED FILE * st, UNUSED UNIT * uptr,
1686 UNUSED int val, UNUSED const void * desc)
1687 {
1688 sim_printf("Number of FNP units in system is %d\n", fnp_dev . numunits);
1689 return SCPE_OK;
1690 }
1691
fnpSetNUnits(UNUSED UNIT * uptr,UNUSED int32 value,const char * cptr,UNUSED void * desc)1692 static t_stat fnpSetNUnits (UNUSED UNIT * uptr, UNUSED int32 value,
1693 const char * cptr, UNUSED void * desc)
1694 {
1695 if (! cptr)
1696 return SCPE_ARG;
1697 int n = atoi (cptr);
1698 if (n < 1 || n > N_FNP_UNITS_MAX)
1699 return SCPE_ARG;
1700 fnp_dev . numunits = (uint32) n;
1701 //return fnppSetNunits (uptr, value, cptr, desc);
1702 return SCPE_OK;
1703 }
1704
fnpShowIPCname(UNUSED FILE * st,UNIT * uptr,UNUSED int val,UNUSED const void * desc)1705 static t_stat fnpShowIPCname (UNUSED FILE * st, UNIT * uptr,
1706 UNUSED int val, UNUSED const void * desc)
1707 {
1708 long n = FNP_UNIT_IDX (uptr);
1709 if (n < 0 || n >= N_FNP_UNITS_MAX)
1710 return SCPE_ARG;
1711 sim_printf("FNP IPC name is %s\n", fnpData.fnpUnitData [n] . ipcName);
1712 return SCPE_OK;
1713 }
1714
fnpSetIPCname(UNIT * uptr,UNUSED int32 value,UNUSED const char * cptr,UNUSED void * desc)1715 static t_stat fnpSetIPCname (UNIT * uptr, UNUSED int32 value,
1716 UNUSED const char * cptr, UNUSED void * desc)
1717 {
1718 long n = FNP_UNIT_IDX (uptr);
1719 if (n < 0 || n >= N_FNP_UNITS_MAX)
1720 return SCPE_ARG;
1721 if (cptr)
1722 {
1723 strncpy (fnpData.fnpUnitData [n] . ipcName, cptr, MAX_DEV_NAME_LEN - 1);
1724 fnpData.fnpUnitData [n] . ipcName [MAX_DEV_NAME_LEN - 1] = 0;
1725 }
1726 else
1727 fnpData.fnpUnitData [n] . ipcName [0] = 0;
1728 return SCPE_OK;
1729 }
1730
fnpShowService(UNUSED FILE * st,UNIT * uptr,UNUSED int val,UNUSED const void * desc)1731 static t_stat fnpShowService (UNUSED FILE * st, UNIT * uptr,
1732 UNUSED int val, UNUSED const void * desc)
1733 {
1734 long devnum = FNP_UNIT_IDX (uptr);
1735 if (devnum < 0 || devnum >= N_FNP_UNITS_MAX)
1736 return SCPE_ARG;
1737 for (uint linenum = 0; linenum < MAX_LINES; linenum ++)
1738 {
1739 enum service_types st = fnpData.fnpUnitData[devnum].MState.line[linenum].service;
1740 switch (st)
1741 {
1742 case service_undefined:
1743 sim_printf("%c.%03d undefined\r\n", 'a' + (int) devnum, linenum);
1744 break;
1745 case service_login:
1746 sim_printf("%c.%03d login\r\n", 'a' + (int) devnum, linenum);
1747 break;
1748 case service_autocall:
1749 sim_printf("%c.%03d autocall\r\n", 'a' + (int) devnum, linenum);
1750 break;
1751 case service_slave:
1752 sim_printf("%c.%03d slave\r\n", 'a' + (int) devnum, linenum);
1753 break;
1754 default:
1755 sim_printf("%d.%03d ERR (%u)\r\n", 'a' + (int) devnum, linenum, st);
1756 break;
1757 }
1758 }
1759 return SCPE_OK;
1760 }
1761
fnpSetService(UNIT * uptr,UNUSED int32 value,const char * cptr,UNUSED void * desc)1762 static t_stat fnpSetService (UNIT * uptr, UNUSED int32 value,
1763 const char * cptr, UNUSED void * desc)
1764 {
1765 if (! cptr)
1766 return SCPE_ARG;
1767 long devnum = FNP_UNIT_IDX (uptr);
1768 if (devnum < 0 || devnum >= N_FNP_UNITS_MAX)
1769 return SCPE_ARG;
1770 // set fnp3 service=30=autocall
1771 // set fnp3 service=31=slave
1772 uint linenum;
1773 char sn [strlen (cptr)];
1774 int nr = sscanf (cptr, "%u=%s", & linenum, sn);
1775 if (nr != 2)
1776 return SCPE_ARG;
1777 if (linenum >= MAX_LINES)
1778 return SCPE_ARG;
1779 if (strcasecmp (sn, "undefined") == 0)
1780 fnpData.fnpUnitData[devnum].MState.line[linenum].service = service_undefined;
1781 else if (strcasecmp (sn, "login") == 0)
1782 fnpData.fnpUnitData[devnum].MState.line[linenum].service = service_login;
1783 else if (strcmp (sn, "ibm3270") == 0)
1784 {
1785 fnpData.fnpUnitData[devnum].MState.line[linenum].service = service_3270;
1786 fnpData.ibm3270ctlr[ASSUME0].fnpno = (uint) devnum;
1787 fnpData.ibm3270ctlr[ASSUME0].lineno = linenum;
1788 }
1789 else if (strcasecmp (sn, "autocall") == 0)
1790 fnpData.fnpUnitData[devnum].MState.line[linenum].service = service_autocall;
1791 else if (strncasecmp (sn, "slave=", 6) == 0)
1792 {
1793 uint pn;
1794 int nr2 = sscanf (sn, "slave=%u", & pn);
1795 if (nr2 != 1)
1796 return SCPE_ARG;
1797 if (pn >= 65535)
1798 return SCPE_ARG;
1799 fnpData.fnpUnitData[devnum].MState.line[linenum].service = service_slave;
1800 fnpData.fnpUnitData[devnum].MState.line[linenum].port = (int) pn;
1801 }
1802 else
1803 return SCPE_ARG;
1804 return SCPE_OK;
1805 }
1806
fnpShowConfig(UNUSED FILE * st,UNIT * uptr,UNUSED int val,UNUSED const void * desc)1807 static t_stat fnpShowConfig (UNUSED FILE * st, UNIT * uptr, UNUSED int val,
1808 UNUSED const void * desc)
1809 {
1810 long fnpUnitIdx = FNP_UNIT_IDX (uptr);
1811 if (fnpUnitIdx >= (long) N_FNP_UNITS_MAX)
1812 {
1813 sim_debug (DBG_ERR, & fnp_dev,
1814 "fnpShowConfig: Invalid unit number %ld\n", fnpUnitIdx);
1815 sim_printf ("error: invalid unit number %ld\n", fnpUnitIdx);
1816 return SCPE_ARG;
1817 }
1818
1819 sim_printf ("FNP unit number %ld\n", fnpUnitIdx);
1820 struct fnpUnitData_s * fudp = fnpData.fnpUnitData + fnpUnitIdx;
1821
1822 sim_printf ("FNP Mailbox Address: %04o(8)\n", fudp -> mailboxAddress);
1823
1824 return SCPE_OK;
1825 }
1826
1827 // SET FNPn FW RESET
1828 // SET FNPn FW ADD <line number list>:<ipaddr>:<ipmask>: ACCEPT | DENY
1829 //
1830 //
1831
1832 int n_fw_entries = 0;
1833 struct fw_entry_s fw_entries [N_FW_ENTRIES];
1834
1835 // [a-h].h[0-9][0-9][0-9]
1836 // terminated by dash or NUL
1837
parse_line(char * line)1838 static int parse_line (char * line)
1839 {
1840 char fnp = line[0];
1841 if (fnp < 'A' || fnp > 'H')
1842 return -1;
1843 int fnpno = fnp - 'A';
1844
1845 if (line [1] != '.')
1846 return -2;
1847
1848 if (line[2] != 'H')
1849 return -3;
1850
1851 if (line[3] < '0' || line[3] > '9')
1852 return -4;
1853
1854 if (line[4] < '0' || line[4] > '9')
1855 return -5;
1856
1857 if (line[5] < '0' || line[5] > '9')
1858 return -6;
1859 int lineno = (line[3] - '0') * 100 +
1860 (line[4] - '0') * 10 +
1861 (line[5] - '0');
1862
1863 if (line[6] != 0 && line[6] != '-')
1864 return -7;
1865
1866 return encodeline (fnpno, lineno);
1867
1868 }
1869
1870 // n.n.n.n
parse_ipaddr(char * str,uint32_t * addr)1871 static int parse_ipaddr (char * str, uint32_t * addr)
1872 {
1873 char * end1, * end2, * end3, * end4;
1874
1875 unsigned long o1 = strtoul (str, & end1, 10);
1876 if (end1 == str || * end1 != '.' || o1 > 255)
1877 return -1;
1878
1879 unsigned long o2 = strtoul (end1 + 1, & end2, 10);
1880 if (end2 == end1 || * end2 != '.' || o2 > 255)
1881 return -2;
1882
1883 unsigned long o3 = strtoul (end2 + 1, & end3, 10);
1884 if (end3 == end2 || * end3 != '.' || o3 > 255)
1885 return -3;
1886
1887 unsigned long o4 = strtoul (end3 + 1, & end4, 10);
1888 if (end4 == end3 || * end4 != 0 || o4 > 255)
1889 return -4;
1890 * addr = (uint32_t) ((o1 << 24) | (o2 << 16) | (o3 << 8) | o4);
1891 return 0;
1892 }
1893
fnpSetFW(UNIT * uptr,UNUSED int32 value,const char * cptr,UNUSED void * desc)1894 static t_stat fnpSetFW (UNIT * uptr, UNUSED int32 value,
1895 const char * cptr, UNUSED void * desc)
1896 {
1897 if (! cptr)
1898 return SCPE_ARG;
1899 long devnum = FNP_UNIT_IDX (uptr);
1900 if (devnum < 0 || devnum >= N_FNP_UNITS_MAX)
1901 return SCPE_ARG;
1902
1903 char sn [strlen (cptr) + 1];
1904 memcpy (sn, cptr, strlen (cptr) + 1);
1905 char * saveptr;
1906 char * tok;
1907
1908 // Parse out ADD/RESET
1909 tok = strtok_r (sn, ":", & saveptr);
1910 if (strcasecmp (tok, "RESET") == 0)
1911 {
1912 n_fw_entries = 0;
1913 sim_printf ("FNP firewall table reset\r\n");
1914 return SCPE_OK;
1915 }
1916
1917 if (strcasecmp (tok, "ADD") == 0)
1918 {
1919 if (n_fw_entries >= N_FW_ENTRIES)
1920 {
1921 sim_printf ("FNP firewall table full\r\n");
1922 return SCPE_ARG;
1923 }
1924 // line range
1925 int line_0, line_1;
1926
1927 tok = strtok_r (NULL, ":", & saveptr);
1928 char * dash = strchr (tok, '-');
1929 if (dash)
1930 {
1931 line_0 = parse_line (tok);
1932 if (line_0 < 0)
1933 {
1934 sim_printf ("Cannot parse first line number\r\n");
1935 return SCPE_ARG;
1936 }
1937
1938 line_1 = parse_line (dash + 1);
1939 if (line_1 < 0)
1940 {
1941 sim_printf ("Cannot parse second line number\r\n");
1942 return SCPE_ARG;
1943 }
1944 if (line_0 > line_1)
1945 {
1946 sim_printf ("line_0 > line_1\r\n");
1947 return SCPE_ARG;
1948 }
1949
1950 }
1951 else
1952 {
1953 line_0 = line_1 = parse_line (tok);
1954 if (line_0 < 0)
1955 {
1956 sim_printf ("Cannot parse line number\r\n");
1957 return SCPE_ARG;
1958 }
1959 }
1960
1961 // parse ipaddr
1962
1963 tok = strtok_r (NULL, ":", & saveptr);
1964 uint32_t ipaddr;
1965 int rc = parse_ipaddr (tok, & ipaddr);
1966 if (rc < 0)
1967 return SCPE_ARG;
1968
1969 // parse cidr
1970
1971
1972 tok = strtok_r (NULL, ":", & saveptr);
1973 char * end;
1974 unsigned long cidr = strtoul (tok, & end, 10);
1975 if (tok == end || * end != 0 || cidr > 32)
1976 return SCPE_OK;
1977 uint32_t cidr_mask = ((uint32_t)-1) << (32-cidr) & MASK32;
1978
1979 // parse accept/deny
1980
1981 bool accept = false;
1982 tok = strtok_r (NULL, ":", & saveptr);
1983 if (strcmp (tok, "ACCEPT") == 0)
1984 accept = true;
1985 else if (strcmp (tok, "DENY") == 0)
1986 accept = false;
1987 else
1988 {
1989 sim_printf ("cannot parse rule ACCEPT/DENY\r\n");
1990 return SCPE_ARG;
1991 }
1992
1993 fw_entries[n_fw_entries].line_0 = (uint) line_0;
1994 fw_entries[n_fw_entries].line_1 = (uint) line_1;
1995 fw_entries[n_fw_entries].ipaddr = ipaddr;
1996 fw_entries[n_fw_entries].cidr = (uint) cidr;
1997 fw_entries[n_fw_entries].cidr_mask = (uint) cidr_mask;
1998 fw_entries[n_fw_entries].accept = accept;
1999 n_fw_entries ++;
2000
2001
2002 return SCPE_OK;
2003 } // ADD
2004
2005
2006 if (strcasecmp (tok, "LIST") == 0)
2007 {
2008 for (int i = 0; i < n_fw_entries; i ++)
2009 {
2010 struct fw_entry_s * p = fw_entries + i;
2011
2012 if (p->line_0 == p->line_1)
2013 {
2014 sim_printf (" %c.h%03d %d.%d.%d.%d/%d %s\r\n",
2015 decodefnp (p->line_0) + 'a',
2016 decodeline (p->line_0),
2017 (p->ipaddr>>24) & 255,
2018 (p->ipaddr>>16) & 255,
2019 (p->ipaddr>>8) & 255,
2020 p->ipaddr & 255,
2021 p->cidr,
2022 p->accept ? "accept" : "deny");
2023 }
2024 else
2025 {
2026 sim_printf (" %c.h%03d-%c.%03d %d.%d.%d.%d/%d %s\r\n",
2027 decodefnp (p->line_0) + 'a',
2028 decodeline (p->line_0),
2029 decodefnp (p->line_1) + 'a',
2030 decodeline (p->line_1),
2031 (p->ipaddr>>24) & 255,
2032 (p->ipaddr>>16) & 255,
2033 (p->ipaddr>>8) & 255,
2034 p->ipaddr & 255,
2035 p->cidr,
2036 p->accept ? "accept" : "deny");
2037 }
2038 }
2039 return SCPE_OK;
2040 }
2041
2042 return SCPE_ARG;
2043 }
2044
fnpShowFW(UNUSED FILE * st,UNIT * uptr,UNUSED int val,UNUSED const void * desc)2045 static t_stat fnpShowFW (UNUSED FILE * st, UNIT * uptr, UNUSED int val,
2046 UNUSED const void * desc)
2047 {
2048 long fnpUnitIdx = FNP_UNIT_IDX (uptr);
2049 if (fnpUnitIdx >= (long) N_FNP_UNITS_MAX)
2050 {
2051 sim_debug (DBG_ERR, & fnp_dev,
2052 "fnpShowConfig: Invalid unit number %ld\n", fnpUnitIdx);
2053 sim_printf ("error: invalid unit number %ld\n", fnpUnitIdx);
2054 return SCPE_ARG;
2055 }
2056 #if 0
2057 sim_printf ("FNP unit number %ld\n", fnpUnitIdx);
2058 struct fnpUnitData_s * fudp = fnpData.fnpUnitData + fnpUnitIdx;
2059
2060 sim_printf ("FNP Mailbox Address: %04o(8)\n", fudp -> mailboxAddress);
2061 #endif
2062 return SCPE_OK;
2063 }
2064
2065
fnpShowStatus(UNUSED FILE * st,UNIT * uptr,UNUSED int val,UNUSED const void * desc)2066 static t_stat fnpShowStatus (UNUSED FILE * st, UNIT * uptr, UNUSED int val,
2067 UNUSED const void * desc)
2068 {
2069 long fnpUnitIdx = FNP_UNIT_IDX (uptr);
2070 if (fnpUnitIdx >= (long) fnp_dev.numunits)
2071 {
2072 sim_debug (DBG_ERR, & fnp_dev,
2073 "fnpShowStatus: Invalid unit number %ld\n", fnpUnitIdx);
2074 sim_printf ("error: invalid unit number %ld\n", fnpUnitIdx);
2075 return SCPE_ARG;
2076 }
2077
2078 sim_printf ("FNP unit number %ld\n", fnpUnitIdx);
2079 struct fnpUnitData_s * fudp = fnpData.fnpUnitData + fnpUnitIdx;
2080
2081 sim_printf ("mailboxAddress: %04o\n", fudp->mailboxAddress);
2082 sim_printf ("fnpIsRunning: %o\n", fudp->fnpIsRunning);
2083 sim_printf ("fnpMBXinUse: %o %o %o %o\n", fudp->fnpMBXinUse[0], fudp->fnpMBXinUse[1], fudp->fnpMBXinUse[2], fudp->fnpMBXinUse[3]);
2084 sim_printf ("lineWaiting: %o %o %o %o\n", fudp->lineWaiting[0], fudp->lineWaiting[1], fudp->lineWaiting[2], fudp->lineWaiting[3]);
2085 sim_printf ("fnpMBXlineno: %o %o %o %o\n", fudp->fnpMBXlineno[0], fudp->fnpMBXlineno[1], fudp->fnpMBXlineno[2], fudp->fnpMBXlineno[3]);
2086 sim_printf ("accept_calls: %o\n", fudp->MState.accept_calls);
2087 for (int l = 0; l < MAX_LINES; l ++)
2088 {
2089 sim_printf ("line: %d\n", l);
2090 sim_printf ("service: %d\n", fudp->MState.line[l].service);
2091 sim_printf ("line_client: %p\n", (void *) fudp->MState.line[l].line_client);
2092 sim_printf ("was_CR: %d\n", fudp->MState.line[l].was_CR);
2093 sim_printf ("listen: %d\n", fudp->MState.line[l].listen);
2094 sim_printf ("inputBufferSize: %d\n", fudp->MState.line[l].inputBufferSize);
2095 sim_printf ("line_break: %d\n", fudp->MState.line[l].line_break);
2096 sim_printf ("send_output: %d\n", fudp->MState.line[l].send_output);
2097 sim_printf ("accept_new_terminal: %d\n", fudp->MState.line[l].accept_new_terminal);
2098 #if DISC_DELAY
2099 sim_printf ("line_disconnected: %d\n", fudp->MState.line[l].line_disconnected);
2100 #else
2101 sim_printf ("line_disconnected: %c\n", fudp->MState.line[l].line_disconnected ? 'T' : 'F');
2102 #endif
2103 sim_printf ("acu_dial_failure: %d\n", fudp->MState.line[l].acu_dial_failure);
2104 sim_printf ("accept_input: %d\n", fudp->MState.line[l].accept_input);
2105 sim_printf ("waitForMbxDone: %d\n", fudp->MState.line[l].waitForMbxDone);
2106 sim_printf ("input_reply_pending: %d\n", fudp->MState.line[l].input_reply_pending);
2107 sim_printf ("input_break: %d\n", fudp->MState.line[l].input_break);
2108 sim_printf ("nPos: %d\n", fudp->MState.line[l].nPos);
2109 sim_printf ("inBuffer: %p\n", (void *) fudp->MState.line[l].inBuffer);
2110 sim_printf ("inSize: %d\n", fudp->MState.line[l].inSize);
2111 sim_printf ("inUsed: %d\n", fudp->MState.line[l].inUsed);
2112 //sim_printf ("doConnect: %p\n", fudp->MState.line[l].doConnect);
2113 //sim_printf ("server: %p\n", fudp->MState.line[l].server);
2114 sim_printf ("port: %d\n", fudp->MState.line[l].port);
2115
2116 }
2117 return SCPE_OK;
2118 }
2119
fnp_show_device_name(UNUSED FILE * st,UNIT * uptr,UNUSED int val,UNUSED const void * desc)2120 static t_stat fnp_show_device_name (UNUSED FILE * st, UNIT * uptr,
2121 UNUSED int val, UNUSED const void * desc)
2122 {
2123 int n = (int) FNP_UNIT_IDX (uptr);
2124 if (n < 0 || n >= N_FNP_UNITS_MAX)
2125 return SCPE_ARG;
2126 sim_printf("Controller device name is %s\n", fnpData.fnpUnitData[n].device_name);
2127 return SCPE_OK;
2128 }
2129
fnp_set_device_name(UNIT * uptr,UNUSED int32 value,const char * cptr,UNUSED void * desc)2130 static t_stat fnp_set_device_name (UNIT * uptr, UNUSED int32 value,
2131 const char * cptr, UNUSED void * desc)
2132 {
2133 int n = (int) FNP_UNIT_IDX (uptr);
2134 if (n < 0 || n >= N_FNP_UNITS_MAX)
2135 return SCPE_ARG;
2136 if (cptr)
2137 {
2138 strncpy (fnpData.fnpUnitData[n].device_name, cptr, MAX_DEV_NAME_LEN-1);
2139 fnpData.fnpUnitData[n].device_name[MAX_DEV_NAME_LEN-1] = 0;
2140 }
2141 else
2142 fnpData.fnpUnitData[n].device_name[0] = 0;
2143 return SCPE_OK;
2144 }
2145
2146
2147 static config_list_t fnp_config_list [] =
2148 {
2149 /* 0 */ { "mailbox", 0, 07777, NULL },
2150 { NULL, 0, 0, NULL }
2151 };
2152
fnpSetConfig(UNIT * uptr,UNUSED int value,const char * cptr,UNUSED void * desc)2153 static t_stat fnpSetConfig (UNIT * uptr, UNUSED int value, const char * cptr, UNUSED void * desc)
2154 {
2155 uint fnpUnitIdx = (uint) FNP_UNIT_IDX (uptr);
2156 //if (fnpUnitIdx >= fnp_dev . numunits)
2157 if (fnpUnitIdx >= N_FNP_UNITS_MAX)
2158 {
2159 sim_debug (DBG_ERR, & fnp_dev, "fnpSetConfig: Invalid unit number %d\n", fnpUnitIdx);
2160 sim_printf ("error: fnpSetConfig: invalid unit number %d\n", fnpUnitIdx);
2161 return SCPE_ARG;
2162 }
2163
2164 struct fnpUnitData_s * fudp = fnpData.fnpUnitData + fnpUnitIdx;
2165
2166 config_state_t cfg_state = { NULL, NULL };
2167
2168 for (;;)
2169 {
2170 int64_t v;
2171 int rc = cfg_parse ("fnpSetConfig", cptr, fnp_config_list, & cfg_state, & v);
2172 switch (rc)
2173 {
2174 case -2: // error
2175 cfg_parse_done (& cfg_state);
2176 return SCPE_ARG;
2177
2178 case -1: // done
2179 break;
2180
2181 case 0: // mailbox
2182 fudp -> mailboxAddress = (uint) v;
2183 break;
2184
2185 default:
2186 sim_printf ("error: fnpSetConfig: invalid cfg_parse rc <%d>\n", rc);
2187 cfg_parse_done (& cfg_state);
2188 return SCPE_ARG;
2189 } // switch
2190 if (rc < 0)
2191 break;
2192 } // process statements
2193 cfg_parse_done (& cfg_state);
2194 return SCPE_OK;
2195 }
2196
2197 #if 0
2198 t_stat fnpLoad (UNUSED int32 arg, const char * buf)
2199 {
2200 FILE * fileref = fopen (buf, "r");
2201 if (! fileref)
2202 {
2203 sim_printf("Couldn't open %s\n", buf);
2204 return SCPE_ARG;
2205 }
2206
2207 char buff [1024];
2208 bool havename = false;
2209 uint devnum = 0;
2210 uint linenum = 0;
2211 while (fgets (buff, sizeof (buff), fileref))
2212 {
2213 char * p = trim (buff); // trim leading and trailing whitespace
2214 if (p [0] == '#') // '#' as first non-white charater is comment line
2215 continue;
2216 if (p [0] == 0) // blank line
2217 continue;;
2218
2219 char * first = trim (Strtok (p, ":")); // stuff to the left of ':'
2220 char * second = trim (Strtok (NULL, ":;")); // stuff to the right of ':'
2221 char dev;
2222 if (strcmp (first, "name") == 0)
2223 {
2224 int n = sscanf (second, "%c.h%u", & dev, & linenum);
2225 if (n != 2 || dev < 'a' || dev > 'h' || linenum > MAX_LINES)
2226 {
2227 sim_printf ("fnpLoad skipping '%s'; n %d dev %c, linenum %u\n", buff, n, dev, linenum);
2228 continue;
2229 }
2230 devnum = (uint) (dev - 'a');
2231 havename = true;
2232 // CMF format sets the default service to login
2233 fnpData.fnpUnitData[devnum].MState.line[linenum].service = service_login;
2234 }
2235 else if (havename && second && strcmp (first, "service") == 0)
2236 {
2237 if (strcmp (second, "login") == 0)
2238 fnpData.fnpUnitData[devnum].MState.line[linenum].service = service_login;
2239 else if (strcmp (second, "autocall") == 0)
2240 fnpData.fnpUnitData[devnum].MState.line[linenum].service = service_autocall;
2241 else if (strcmp (second, "slave") == 0)
2242 fnpData.fnpUnitData[devnum].MState.line[linenum].service = service_slave;
2243 else if (strcmp (second, "offline") == 0)
2244 fnpData.fnpUnitData[devnum].MState.line[linenum].service = service_undefined;
2245 else
2246 sim_printf ("service type '%s' not recognized; skipping\n", second);
2247 }
2248 // This is not part of the CMF language, but I need away to set some additional
2249 // parameters
2250 else if (havename && second && strcmp(first, "port") == 0)
2251 {
2252 trim (second);
2253 char * end;
2254 long port = strtol (second, & end, 0);
2255 if (* end || port < 0 || port >= 65535)
2256 {
2257 sim_printf ("can't parse fromport '%s'; ignored\n", second);
2258 }
2259 else
2260 {
2261 fnpData.fnpUnitData[devnum].MState.line[linenum].port = (int) port;
2262 }
2263 //sim_printf ("%s fromport %d\n", current->multics.name, current->multics.fromport);
2264 }
2265
2266 else if (strcmp (first, "end;") == 0)
2267 {
2268 break;
2269 }
2270
2271
2272 // Ingored
2273 else if (strcmp (first, "Service") == 0 ||
2274 strcmp (first, "Charge") == 0 ||
2275 strcmp (first, "Terminal_type") == 0 ||
2276 strcmp (first, "Line_type") == 0 ||
2277 strcmp (first, "Baud") == 0 ||
2278 strcmp (first, "FNP_required_up_time") == 0 ||
2279 strcmp (first, "FNP") == 0 ||
2280 strcmp (first, "type") == 0 ||
2281 strcmp (first, "memory") == 0 ||
2282 strcmp (first, "lsla") == 0 ||
2283 strcmp (first, "hsla") == 0 ||
2284 strcmp (first, "image") == 0 ||
2285 strcmp (first, "service") == 0 ||
2286 strcmp (first, "attributes") == 0)
2287 {
2288 // Ignored
2289 }
2290 else
2291 sim_printf ("fnpLoad '%s' not recognized; skipping\n", buff);
2292 }
2293 fclose (fileref);
2294 return SCPE_OK;
2295 }
2296 #endif
2297
set_fnp_server_port(UNUSED int32 arg,const char * buf)2298 t_stat set_fnp_server_port (UNUSED int32 arg, const char * buf)
2299 {
2300 if (! buf)
2301 return SCPE_ARG;
2302 int n = atoi (buf);
2303 if (n < 1 || n > 65535)
2304 return SCPE_ARG;
2305 fnpData.telnet_port = n;
2306 if (!sim_quiet)
2307 {
2308 sim_printf ("FNP telnet server port set to %d\n", n);
2309 }
2310 return SCPE_OK;
2311 }
2312
set_fnp_server_address(UNUSED int32 arg,const char * buf)2313 t_stat set_fnp_server_address (UNUSED int32 arg, const char * buf)
2314 {
2315 if (fnpData.telnet_address)
2316 free (fnpData.telnet_address);
2317 fnpData.telnet_address = strdup (buf);
2318 if (!sim_quiet)
2319 {
2320 sim_printf ("FNP telnet server address set to %s\n", fnpData.telnet_address);
2321 }
2322 return SCPE_OK;
2323 }
2324
set_fnp_3270_server_port(UNUSED int32 arg,const char * buf)2325 t_stat set_fnp_3270_server_port (UNUSED int32 arg, const char * buf)
2326 {
2327 if (! buf)
2328 return SCPE_ARG;
2329 int n = atoi (buf);
2330 if (n < 1 || n > 65535)
2331 return SCPE_ARG;
2332 fnpData.telnet3270_port = n;
2333 if (!sim_quiet)
2334 {
2335 sim_printf ("FNP telnet3270 server port set to %d\n", n);
2336 }
2337 return SCPE_OK;
2338 }
2339
fnp_start(UNUSED int32 arg,UNUSED const char * buf)2340 t_stat fnp_start (UNUSED int32 arg, UNUSED const char * buf)
2341 {
2342 if (!sim_quiet)
2343 {
2344 sim_printf ("FNP force start\n");
2345 }
2346 fnpuvInit (fnpData.telnet_port, fnpData.telnet_address);
2347 //fnpuv3270Init (fnpData.telnet3270_port);
2348 return SCPE_OK;
2349 }
2350
2351
2352 #define PROMPT "HSLA Port ("
2353
fnpConnectPrompt(uv_tcp_t * client)2354 void fnpConnectPrompt (uv_tcp_t * client)
2355 {
2356 fnpuv_start_writestr (client, (unsigned char *) PROMPT);
2357 bool first = true;
2358 uint numunits = (uint) fnp_dev.numunits;
2359 for (uint fnp_unit_idx = 0; fnp_unit_idx < numunits; fnp_unit_idx ++)
2360 {
2361 if (! fnpData.fnpUnitData[fnp_unit_idx].fnpIsRunning)
2362 continue;
2363 for (uint lineno = 0; lineno < MAX_LINES; lineno ++)
2364 {
2365 struct t_line * linep = & fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno];
2366 if (! linep->listen)
2367 continue;
2368 if (linep->service == service_login &&
2369 ! linep->line_disconnected &&
2370 ! linep->line_client &&
2371 fnpData.fnpUnitData[fnp_unit_idx].MState.accept_calls)
2372 {
2373 if (! first)
2374 fnpuv_start_writestr (client, (unsigned char *) ",");
2375 char name [16];
2376 first = false;
2377 sprintf (name, "%c.h%03d", 'a' + fnp_unit_idx, lineno);
2378 fnpuv_start_writestr (client, (unsigned char *) name);
2379 }
2380 }
2381 }
2382 fnpuv_start_writestr (client, (unsigned char *) ")? ");
2383 }
2384
2385 // http://www8.cs.umu.se/~isak/Snippets/a2e.c
2386
2387 /*
2388 ** ASCII <=> EBCDIC conversion functions
2389 */
2390
2391 const unsigned char a2e[256] = {
2392 0, 1, 2, 3, 55, 45, 46, 47, 22, 5, 37, 11, 12, 13, 14, 15,
2393 16, 17, 18, 19, 60, 61, 50, 38, 24, 25, 63, 39, 28, 29, 30, 31,
2394 64, 79,127,123, 91,108, 80,125, 77, 93, 92, 78,107, 96, 75, 97,
2395 240,241,242,243,244,245,246,247,248,249,122, 94, 76,126,110,111,
2396 124,193,194,195,196,197,198,199,200,201,209,210,211,212,213,214,
2397 215,216,217,226,227,228,229,230,231,232,233, 74,224, 90, 95,109,
2398 121,129,130,131,132,133,134,135,136,137,145,146,147,148,149,150,
2399 151,152,153,162,163,164,165,166,167,168,169,192,106,208,161, 7,
2400 32, 33, 34, 35, 36, 21, 6, 23, 40, 41, 42, 43, 44, 9, 10, 27,
2401 48, 49, 26, 51, 52, 53, 54, 8, 56, 57, 58, 59, 4, 20, 62,225,
2402 65, 66, 67, 68, 69, 70, 71, 72, 73, 81, 82, 83, 84, 85, 86, 87,
2403 88, 89, 98, 99,100,101,102,103,104,105,112,113,114,115,116,117,
2404 118,119,120,128,138,139,140,141,142,143,144,154,155,156,157,158,
2405 159,160,170,171,172,173,174,175,176,177,178,179,180,181,182,183,
2406 184,185,186,187,188,189,190,191,202,203,204,205,206,207,218,219,
2407 220,221,222,223,234,235,236,237,238,239,250,251,252,253,254,255
2408 };
2409
2410 const unsigned char e2a[256] = {
2411 0, 1, 2, 3,156, 9,134,127,151,141,142, 11, 12, 13, 14, 15,
2412 16, 17, 18, 19,157,133, 8,135, 24, 25,146,143, 28, 29, 30, 31,
2413 128,129,130,131,132, 10, 23, 27,136,137,138,139,140, 5, 6, 7,
2414 144,145, 22,147,148,149,150, 4,152,153,154,155, 20, 21,158, 26,
2415 32,160,161,162,163,164,165,166,167,168, 91, 46, 60, 40, 43, 33,
2416 38,169,170,171,172,173,174,175,176,177, 93, 36, 42, 41, 59, 94,
2417 45, 47,178,179,180,181,182,183,184,185,124, 44, 37, 95, 62, 63,
2418 186,187,188,189,190,191,192,193,194, 96, 58, 35, 64, 39, 61, 34,
2419 195, 97, 98, 99,100,101,102,103,104,105,196,197,198,199,200,201,
2420 202,106,107,108,109,110,111,112,113,114,203,204,205,206,207,208,
2421 209,126,115,116,117,118,119,120,121,122,210,211,212,213,214,215,
2422 216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,
2423 123, 65, 66, 67, 68, 69, 70, 71, 72, 73,232,233,234,235,236,237,
2424 125, 74, 75, 76, 77, 78, 79, 80, 81, 82,238,239,240,241,242,243,
2425 92,159, 83, 84, 85, 86, 87, 88, 89, 90,244,245,246,247,248,249,
2426 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,250,251,252,253,254,255
2427 };
2428
2429 #if 0
2430 static char ASCIItoEBCDIC(const unsigned char c)
2431 {
2432 return a2e[c];
2433 }
2434
2435 static char EBCDICtoASCII(const unsigned char c)
2436 {
2437 return e2a[c];
2438 }
2439 #endif
2440
fnp3270Msg(uv_tcp_t * client,unsigned char * msg)2441 static void fnp3270Msg (uv_tcp_t * client, unsigned char * msg)
2442 {
2443 //sim_printf ("%s", msg);
2444 size_t l = strlen ((char *) msg);
2445 unsigned char buf [l];
2446 for (uint i = 0; i < l; i ++)
2447 buf[i] = a2e[msg[i]];
2448 // command Erase write 245 (xf5)
2449 // WCC 66 x42 0100 0010 Reset, KB restore
2450 // SBA 17 x11
2451 // 1st addr byte 64
2452 // 2nd addr byte 64
2453 // start field 29 x1D
2454 // arg 96
2455 // 29, 200, 133, 153, 131, 164, 147, 133 ???
2456 //unsigned char EW [] = {245, 66, 17, 64, 64 };
2457 unsigned char EW [] = {245, 0xc3, 17, 64, 64 };
2458 fnpuv_start_3270_write (client, EW, sizeof (EW));
2459 fnpuv_start_3270_write (client, buf, (ssize_t) l);
2460 fnpuv_send_eor (client);
2461 }
2462
fnp3270ConnectPrompt(uv_tcp_t * client)2463 void fnp3270ConnectPrompt (uv_tcp_t * client)
2464 {
2465 if (! client || ! client->data)
2466 {
2467 sim_warn ("fnp3270ConnectPrompt bad client data\r\n");
2468 return;
2469 }
2470 uint fnpno = fnpData.ibm3270ctlr[ASSUME0].fnpno;
2471 uint lineno = fnpData.ibm3270ctlr[ASSUME0].lineno;
2472 //struct t_line * linep = & fnpData.fnpUnitData[fnpno].MState.line[lineno];
2473 uvClientData * p = client->data;
2474 p->assoc = true;
2475 p->fnpno = fnpno;
2476 p->lineno = lineno;
2477 //fnpData.fnpUnitData[fnpno].MState.line[lineno].line_client = client;
2478
2479 #if 1
2480 // Don't know ttype yet because Telnet negotiation won't
2481 // start until evPoll runs.
2482 unsigned char buf [256];
2483 sprintf ((char *) buf, "DPS8/M 3270 connection to %c.%03d.%d ttype %s\n", fnpno+'a',lineno, p->stationNo, p->ttype);
2484 fnpData.ibm3270ctlr[ASSUME0].selDevChar = addr_map[p->stationNo];
2485 fnp3270Msg (client, buf);
2486 #endif
2487 }
2488
processLineInput(uv_tcp_t * client,unsigned char * buf,ssize_t nread)2489 void processLineInput (uv_tcp_t * client, unsigned char * buf, ssize_t nread)
2490 {
2491 if (! client || ! client->data)
2492 {
2493 sim_warn ("processLineInput bad client data\r\n");
2494 return;
2495 }
2496 uvClientData * p = (uvClientData *) client->data;
2497 uint fnpno = p -> fnpno;
2498 uint lineno = p -> lineno;
2499 if (fnpno >= N_FNP_UNITS_MAX || lineno >= MAX_LINES)
2500 {
2501 sim_printf ("bogus client data\n");
2502 return;
2503 }
2504 //sim_printf ("assoc. %d.%d nread %ld\n", fnpno, lineno, nread);
2505 //{for (int i = 0; i < nread; i ++) sim_printf ("%c", isgraph (e2a[buf[i]]) ? e2a[buf[i]] : '.');
2506 //sim_printf ("\n");
2507 //for (int i = 0; i < nread; i ++) sim_printf (" %02x", buf[i]);
2508 //sim_printf ("\r\n");
2509 //}
2510
2511 struct t_line * linep = & fnpData.fnpUnitData[fnpno].MState.line[lineno];
2512
2513 // By design, inBuffer overun shouldn't happen, but it has been seen in IMFT.
2514 // (When the TCP backs up, the buffers are merged so that larger and larger
2515 // reads occur. When the backedup buffer exceeds 65536, libev calls the read
2516 // callback twice in a row, once with the first 65536, and the next with the
2517 // remaining.
2518 // Cope with it my realloc'ing the buffer and appending the new data. Ugh.
2519 if (linep->inBuffer)
2520 {
2521 sim_warn ("inBuffer overrun\n");
2522 unsigned char * new = realloc (linep->inBuffer, (unsigned long) (linep->inSize + nread));
2523 if (! new)
2524 {
2525 sim_warn ("inBuffer realloc fail; dropping data\n");
2526 goto done;
2527 }
2528 memcpy (new + linep->inSize, buf, (unsigned long) nread);
2529 linep->inSize += nread;
2530 linep->inBuffer = new;
2531 }
2532 else
2533 {
2534 linep->inBuffer = malloc ((unsigned long) nread);
2535 if (! linep->inBuffer)
2536 {
2537 sim_warn ("inBuffer malloc fail; dropping data\n");
2538 goto done;
2539 }
2540 memcpy (linep->inBuffer, buf, (unsigned long) nread);
2541 linep->inSize = (uint) nread;
2542 linep->inUsed = 0;
2543 }
2544
2545 done:;
2546 // Prevent further reading until this buffer is consumed
2547 fnpuv_read_stop (client);
2548 }
2549
process3270Input(uv_tcp_t * client,unsigned char * buf,ssize_t nread)2550 void process3270Input (uv_tcp_t * client, unsigned char * buf, ssize_t nread)
2551 {
2552 if (! client || ! client->data)
2553 {
2554 sim_warn ("process3270Input bad client data\r\n");
2555 return;
2556 }
2557 uvClientData * p = (uvClientData *) client->data;
2558 uint fnpno = p->fnpno;
2559 uint lineno = p->lineno;
2560 uint stn_no = p->stationNo;
2561
2562 if (fnpno >= N_FNP_UNITS_MAX || lineno >= MAX_LINES)
2563 {
2564 sim_printf ("bogus client data\n");
2565 return;
2566 }
2567 if_sim_debug (DBG_TRACE, & fnp_dev) {
2568 sim_debug (DBG_TRACE, & fnp_dev, "process3270Input nread %ld\n", (long)nread);
2569 for (int i = 0; i < nread; i ++) sim_debug (DBG_TRACE, & fnp_dev, "%c", isgraph (e2a[buf[i]]) ? e2a[buf[i]] : '.');
2570 sim_debug (DBG_TRACE, & fnp_dev, "\r\n");
2571 for (int i = 0; i < nread; i ++) sim_debug (DBG_TRACE, & fnp_dev, " %02x", buf[i]);
2572 sim_debug (DBG_TRACE, & fnp_dev, "\r\n");
2573 }
2574
2575 struct t_line * linep = & fnpData.fnpUnitData[fnpno].MState.line[lineno];
2576 if (! fnpData.fnpUnitData[fnpno].MState.accept_calls)
2577 {
2578 if (! linep->inBuffer)
2579 fnp3270Msg (client, (unsigned char *) "Multics is not accepting calls\r\n");
2580 return;
2581 }
2582 if (! linep->listen)
2583 {
2584 if (! linep->inBuffer)
2585 fnp3270Msg (client, (unsigned char *) "Multics is not listening to this line\r\n");
2586 return;
2587 }
2588
2589 // By design, inBuffer overun shouldn't happen, but it has been seen in IMFT.
2590 // (When the TCP backs up, the buffers are merged so that larger and larger
2591 // reads occur. When the backedup buffer exceeds 65536, libev calls the read
2592 // callback twice in a row, once with the first 65536, and the next with the
2593 // remaining.
2594 // Cope with it my realloc'ing the buffer and appending the new data. Ugh.
2595
2596 struct station_s * stn_p = & fnpData.ibm3270ctlr[ASSUME0].stations[stn_no];
2597 if (stn_p->stn_in_buffer)
2598 {
2599 sim_warn ("stn_in_buffer overrun\n");
2600 unsigned char * new = realloc (stn_p->stn_in_buffer, (unsigned long) (stn_p->stn_in_size + nread));
2601 if (! new)
2602 {
2603 sim_warn ("stn_in_buffer realloc fail; dropping data\n");
2604 goto done;
2605 }
2606 memcpy (new + stn_p->stn_in_size, buf, (unsigned long) nread);
2607 stn_p->stn_in_size += nread;
2608 stn_p->stn_in_buffer = new;
2609 }
2610 else
2611 {
2612 stn_p->stn_in_buffer = malloc ((unsigned long) nread);
2613 if (! stn_p->stn_in_buffer)
2614 {
2615 sim_warn ("stn_in_buffer malloc fail; dropping data\n");
2616 goto done;
2617 }
2618 memcpy (stn_p->stn_in_buffer, buf, (unsigned long) nread);
2619 stn_p->stn_in_size = (uint) nread;
2620 stn_p->stn_in_used = 0;
2621 }
2622
2623 sim_debug (DBG_TRACE, & fnp_dev, "process3270Input stashed %lu bytes in stn %u; stn_in_size now %u\n", (unsigned long)nread, stn_no, stn_p->stn_in_size);
2624 done:;
2625 // Prevent further reading until this buffer is consumed
2626 // Rely on 3270 keyboard logic protocol to prevent buffer collision
2627 //fnpuv_read_stop (client);
2628 }
2629
reset_line(struct t_line * linep)2630 void reset_line (struct t_line * linep)
2631 {
2632 linep->was_CR = false;
2633 linep->inputBufferSize = 0;
2634 linep->ctrlStrIdx = 0;
2635 linep->breakAll = false;
2636 linep->handleQuit = false;
2637 linep->echoPlex = false;
2638 linep->crecho = false;
2639 linep->lfecho = false;
2640 linep->tabecho = false;
2641 linep->replay = false;
2642 linep->polite = false;
2643 linep->prefixnl = false;
2644 linep->eight_bit_out = false;
2645 linep->eight_bit_in = false;
2646 linep->odd_parity = false;
2647 linep->output_flow_control = false;
2648 linep->input_flow_control = false;
2649 linep->block_xfer_in_frame_sz = 0;
2650 linep->block_xfer_out_frame_sz = 0;
2651 memset (linep->delay_table, 0, sizeof (linep->delay_table));
2652 linep->inputSuspendLen = 0;
2653 memset (linep->inputSuspendStr, 0, sizeof (linep->inputSuspendStr));
2654 linep->inputResumeLen = 0;
2655 memset (linep->inputResumeStr, 0, sizeof (linep->inputResumeStr));
2656 linep->outputSuspendLen = 0;
2657 memset (linep->outputSuspendStr, 0, sizeof (linep->outputSuspendStr));
2658 linep->outputResumeLen = 0;
2659 memset (linep->outputResumeStr, 0, sizeof (linep->outputResumeStr));
2660 linep->frame_begin = 0;
2661 linep->frame_end = 0;
2662 memset (linep->echnego_break_table, 0, sizeof (linep->echnego_break_table));
2663 linep->echnego_sync_ctr = 0;
2664 linep->echnego_screen_left = 0;
2665 linep->echnego_unechoed_cnt = 0;
2666 linep->echnego_on = false;
2667 linep->echnego_synced = false;
2668 linep->line_break = false;
2669 }
2670
processUserInput(uv_tcp_t * client,unsigned char * buf,ssize_t nread)2671 void processUserInput (uv_tcp_t * client, unsigned char * buf, ssize_t nread)
2672 {
2673 if (! client || ! client->data)
2674 {
2675 sim_warn ("processUserInput bad client data\r\n");
2676 return;
2677 }
2678 uvClientData * p = (uvClientData *) client->data;
2679 for (ssize_t nchar = 0; nchar < nread; nchar ++)
2680 {
2681 unsigned char kar = buf [nchar];
2682
2683 if (kar == 0x1b || kar == 0x03) // ESCape ('\e') | ^C
2684 {
2685 close_connection ((uv_stream_t *) client);
2686 return;
2687 }
2688
2689 // buffer too full for anything more?
2690 if (p->nPos >= sizeof(p->buffer) - 1)
2691 {
2692 // yes. Only allow \n, \r, ^H, ^R
2693 switch (kar)
2694 {
2695 case '\b': // backspace
2696 case 127: // delete
2697 {
2698 if (p->nPos)
2699 {
2700 fnpuv_start_writestr (client, (unsigned char *) "\b \b"); // remove char from line
2701 p->buffer[p->nPos] = 0; // remove char from buffer
2702 p->nPos -= 1; // back up buffer pointer
2703 }
2704 }
2705 break;
2706
2707 case '\n':
2708 case '\r':
2709 {
2710 p->buffer[p->nPos] = 0;
2711 goto check;
2712 }
2713
2714 case 0x12: // ^R
2715 {
2716 fnpuv_start_writestr (client, (unsigned char *) "^R\r\n"); // echo ^R
2717 fnpConnectPrompt (client);
2718 fnpuv_start_writestr (client, (unsigned char *) p->buffer);
2719 }
2720 break;
2721
2722 default:
2723 break;
2724 } // switch kar
2725 continue; // process next character in buffer
2726 } // if buffer full
2727
2728 if (isprint (kar)) // printable?
2729 {
2730 unsigned char str [2] = { kar, 0 };
2731 fnpuv_start_writestr (client, str);
2732 p->buffer[p->nPos++] = (char) kar;
2733 }
2734 else
2735 {
2736 switch (kar)
2737 {
2738 case '\b': // backspace
2739 case 127: // delete
2740 {
2741 if (p->nPos)
2742 {
2743 fnpuv_start_writestr (client, (unsigned char *) "\b \b"); // remove char from line
2744 p->buffer[p->nPos] = 0; // remove char from buffer
2745 p->nPos -= 1; // back up buffer pointer
2746 }
2747 }
2748 break;
2749
2750 case '\n':
2751 case '\r':
2752 {
2753 p->buffer[p->nPos] = 0;
2754 goto check;
2755 }
2756
2757 case 0x12: // ^R
2758 {
2759 fnpuv_start_writestr (client, (unsigned char *) "^R\r\n"); // echo ^R
2760 fnpConnectPrompt (client);
2761 fnpuv_start_writestr (client, (unsigned char *) p->buffer);
2762 }
2763 break;
2764
2765 default:
2766 break;
2767 } // switch kar
2768 } // not printable
2769 } // for nchar
2770 return;
2771
2772 check:;
2773 char cpy [p->nPos + 1];
2774 memcpy (cpy, p->buffer, p->nPos);
2775 cpy [p->nPos] = 0;
2776 trim (cpy);
2777 sim_printf ("<%s>", cpy);
2778 p->nPos = 0;
2779 fnpuv_start_writestr (client, (unsigned char *) "\r\n");
2780
2781
2782 uint fnp_unit_idx = 0;
2783 uint lineno = 0;
2784
2785 if (strlen (cpy))
2786 {
2787 char fnpcode;
2788 int cnt = sscanf (cpy, "%c.h%u", & fnpcode, & lineno);
2789 //sim_printf ("cnt %d fnpcode %c lineno %d\n", cnt, fnpcode, lineno);
2790 if (cnt != 2 || fnpcode < 'a' || fnpcode > 'h' || lineno >= MAX_LINES)
2791 {
2792 fnpuv_start_writestr (client, (unsigned char *) "can't parse\r\n");
2793 goto reprompt;
2794 }
2795 fnp_unit_idx = (uint) (fnpcode - 'a');
2796 if (fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno].service != service_login ||
2797 fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno].line_client)
2798 {
2799 fnpuv_start_writestr (client, (unsigned char *) "not availible\r\n");
2800 goto reprompt;
2801 }
2802 goto associate;
2803 }
2804 else
2805 {
2806 uint32 numunits = fnp_dev.numunits;
2807 for (fnp_unit_idx = 0; fnp_unit_idx < numunits; fnp_unit_idx ++)
2808 {
2809 if (! fnpData.fnpUnitData[fnp_unit_idx].fnpIsRunning)
2810 continue;
2811 for (lineno = 0; lineno < MAX_LINES; lineno ++)
2812 {
2813 if (fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno].service == service_login &&
2814 fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno].listen &&
2815 ! fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno].line_disconnected &&
2816 ! fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno].line_client &&
2817 fnpData.fnpUnitData[fnp_unit_idx].MState.accept_calls)
2818 {
2819 goto associate;
2820 }
2821 }
2822 }
2823 fnpuv_start_writestr (client, (unsigned char *) "not available\r\n");
2824 goto reprompt;
2825 }
2826 reprompt:;
2827 fnpConnectPrompt (client);
2828 return;
2829
2830 associate:;
2831
2832 fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno].line_client = client;
2833 //sim_printf ("associated %c.%03d %p\n", fnp_unit_idx + 'a', lineno, client);
2834 p->assoc = true;
2835 p->fnpno = fnp_unit_idx;
2836 p->lineno = lineno;
2837 p->read_cb = fnpuv_associated_readcb;
2838 p->write_cb = fnpuv_start_write;
2839 p->write_actual_cb = fnpuv_start_write_actual;
2840 // Only enable read when Multics can accept it.
2841 //uv_read_stop ((uv_stream_t *) client);
2842
2843 char buf2 [1024];
2844
2845 struct sockaddr name;
2846 int namelen = sizeof (name);
2847 int ret = uv_tcp_getpeername (client, & name, & namelen);
2848 if (ret < 0)
2849 {
2850 sim_printf ("CONNECT (addr err %d) to %c.h%03d\n", ret, fnp_unit_idx +'a', lineno);
2851 }
2852 else
2853 {
2854 struct sockaddr_in * p = (struct sockaddr_in *) & name;
2855 sim_printf ("CONNECT %s to %c.h%03d\n", inet_ntoa (p -> sin_addr), fnp_unit_idx +'a', lineno);
2856 }
2857
2858 sprintf (buf2, "Attached to line %c.h%03d\r\n", fnp_unit_idx +'a', lineno);
2859 fnpuv_start_writestr (client, (unsigned char *) buf2);
2860
2861 if (! fnpData.fnpUnitData[fnp_unit_idx].MState.accept_calls)
2862 fnpuv_start_writestr (client, (unsigned char *) "Multics is not accepting calls\r\n");
2863 else if (! fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno].listen)
2864 fnpuv_start_writestr (client, (unsigned char *) "Multics is not listening to this line\r\n");
2865
2866 // Set from CMF data now.
2867 //fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno].lineType = 1 /* LINE_ASCII */;
2868 if (fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno].lineType == 0) /* LINE_NONE */
2869 fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno].lineType = 1; /* LINE_ASCII */
2870 fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno].accept_new_terminal = true;
2871 reset_line (& fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno]);
2872 }
2873
startFNPListener(void)2874 void startFNPListener (void)
2875 {
2876 fnpuvInit (fnpData.telnet_port, fnpData.telnet_address);
2877 }
2878