root/src/dps8/dps8_fnp2.c

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

DEFINITIONS

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

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

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