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

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