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

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