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