root/src/dps8/dps8_fnp2.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. setTIMW
  2. fnpInit
  3. fnpExit
  4. fnpReset
  5. findMbx
  6. notifyCS
  7. fnp_rcd_ack_echnego_init
  8. fnp_rcd_ack_echnego_stop
  9. fnp_rcd_line_disconnected
  10. fnp_rcd_input_in_mailbox
  11. fnp_rcd_line_status
  12. fnp_rcd_accept_input
  13. fnp_rcd_line_break
  14. fnp_rcd_send_output
  15. fnp_rcd_acu_dial_failure
  16. fnp_rcd_accept_new_terminal
  17. fnp_rcd_wru_timeout
  18. processInputCharacter
  19. fnpRecvEOR
  20. fnpProcessBuffer
  21. fnpProcessBuffers
  22. set_3270_write_complete
  23. send_3270_msg
  24. send_stn_in_buffer
  25. fnp_process_3270_event
  26. fnpProcessEvent
  27. fnpShowNUnits
  28. fnpSetNUnits
  29. fnpShowIPCname
  30. fnpSetIPCname
  31. fnpShowService
  32. fnpSetService
  33. fnpShowConfig
  34. parse_line
  35. parse_ipaddr
  36. fnpSetFW
  37. fnpShowFW
  38. fnpShowStatus
  39. fnp_show_device_name
  40. fnp_set_device_name
  41. fnpSetConfig
  42. set_fnp_server_port
  43. set_fnp_server_address
  44. set_fnp_3270_server_port
  45. fnp_start
  46. fnpConnectPrompt
  47. fnp3270Msg
  48. fnp3270ConnectPrompt
  49. processLineInput
  50. process3270Input
  51. reset_line
  52. processUserInput
  53. startFNPListener

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

/* [previous][next][first][last][top][bottom][index][help] */