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 "../simh/sim_defs.h"
 100 #include "../simh/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\r\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\r\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\r\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\r\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\r\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\r\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\r\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\r\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\r\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\r\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\r\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\r\n", lineno);
 704     //sim_printf ("acu_dial_failure %d %d %d\r\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\r\n", lineno);
 727     //sim_printf ("accept_new_terminal %d %d %d\r\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\r\n", lineno);
 795     //sim_printf ("wru_timeout %d %d %d\r\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\r\n", kar, !!p->telnetp,
 832         //    linep->was_CR, kar == 0);
 833 //sim_printf ("%03o %c\r\n", kar, isgraph(kar)? kar : '#');
 834         if (p && p->telnetp && linep->was_CR && kar == 0)
 835           {
 836             //sim_printf ("dropping nul\r\n");
 837             linep->was_CR = false;
 838             return false;
 839           }
 840         linep->was_CR = kar == 015;
 841         //sim_printf ("was CR %d\r\n", linep->was_CR);
 842       }
 843 
 844 //sim_printf ("%03o %c\r\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 ("\r\nkar <%c>\r\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\r\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\r\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\r\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\r\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     if (p->stationNo > IBM3270_STATIONS_MAX - 1)
1225         return;
1226     fnpData.ibm3270ctlr[ASSUME0].stations[p->stationNo].EORReceived = true;
1227     fnpData.ibm3270ctlr[ASSUME0].stations[p->stationNo].hdr_sent    = false;
1228   }
1229 
1230 static void fnpProcessBuffer (struct t_line * linep)
     /* [previous][next][first][last][top][bottom][index][help] */
1231   {
1232     // The connection could have closed when we were not looking
1233 #if defined(TUN)
1234     if ((! linep->is_tun) && ! linep->line_client)
1235 #else
1236     if (! linep->line_client)
1237 #endif /* if defined(TUN) */
1238       {
1239         if (linep->inBuffer)
1240           FREE (linep->inBuffer);
1241         linep->inBuffer = NULL;
1242         linep->inSize   = 0;
1243         linep->inUsed   = 0;
1244         return;
1245       }
1246 
1247     while (linep->inBuffer && linep->inUsed < linep->inSize)
1248        {
1249          unsigned char c = linep->inBuffer [linep->inUsed ++];
1250 //sim_printf ("processing %d/%d %o '%c'\r\n", linep->inUsed-1, linep->inSize, c, isprint (c) ? c : '?');
1251          bool eob = linep->inUsed >= linep->inSize;
1252          if (eob)
1253            {
1254              FREE (linep->inBuffer);
1255              linep->inBuffer = NULL;
1256              linep->inSize   = 0;
1257              linep->inUsed   = 0;
1258              // The connection could have been closed when we weren't looking
1259              if (linep->line_client)
1260                fnpuv_read_start (linep->line_client);
1261            }
1262          if (linep->service == service_3270)
1263            {
1264              linep->buffer[linep->nPos++] = c;
1265              linep->buffer[linep->nPos]   = 0;
1266              continue;
1267            }
1268          if (processInputCharacter (linep, c, eob))
1269            break;
1270        }
1271   }
1272 
1273 static void fnpProcessBuffers (void)
     /* [previous][next][first][last][top][bottom][index][help] */
1274   {
1275     uint numunits = (uint) fnp_dev.numunits;
1276     for (uint fnp_unit_idx = 0; fnp_unit_idx < numunits; fnp_unit_idx ++)
1277       {
1278         if (! fnpData.fnpUnitData[fnp_unit_idx].fnpIsRunning)
1279           continue;
1280         for (uint lineno = 0; lineno < MAX_LINES; lineno ++)
1281           {
1282             struct t_line * linep = & fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno];
1283 
1284             // If an accept_input request is posted, then buffer is busy.
1285             if (linep->accept_input)
1286               continue;
1287 
1288             // If a input command ack is pending, then buffer is busy.
1289             if (linep->input_reply_pending)
1290               continue;
1291 
1292             // If no data to process
1293             if (!linep->inBuffer)
1294                continue;
1295 
1296             fnpProcessBuffer (linep);
1297           }
1298       }
1299   }
1300 
1301 //  dcl 1 line_stat aligned,
1302 //      2 op fixed binary (17) unaligned,                       /* contains reason for status */
1303 //      2 val (3) fixed binary (17) unaligned;
1304 
1305 //  /* Values for line_stat.op */
1306 //
1307 //  dcl (BID_FAILED                    initial (1),
1308 //       BAD_BLOCK                     initial (2),
1309 //       REVERSE_INTERRUPT             initial (3),
1310 //       TOO_MANY_NAKS                 initial (4),
1311 //       FNP_WRITE_STATUS              initial (5),
1312 //       IBM3270_WRITE_COMPLETE        initial (6),
1313 //       IBM3270_WACK_MESSAGE          initial (7),
1314 //       IBM3270_WRITE_EOT             initial (8),
1315 //       IBM3270_WRITE_ABORT           initial (9),
1316 //       IBM3270_SELECT_FAILED         initial (10),
1317 //       IBM3270_WACK_SELECT           initial (11),
1318 //       IBM3270_NAK_OUTPUT            initial (12),
1319 //       HASP_INIT_COMPLETE            initial (13),
1320 //       HASP_FOREIGN_SWAB_RESET       initial (14))
1321 //            fixed binary static options (constant);
1322 
1323 // Send a message to Multics
1324 
1325 void set_3270_write_complete (UNUSED uv_tcp_t * client)
     /* [previous][next][first][last][top][bottom][index][help] */
1326   {
1327 #if defined(TESTING)
1328     cpu_state_t * cpup = _cpup;
1329 #endif
1330     //uvClientData * p = client->data;
1331 //sim_printf ("set_3270_write_complete %p stn_no %d\r\n", p, p->stationNo);
1332     sim_debug (DBG_TRACE, & fnp_dev, "set_3270_write_complete\r\n");
1333     //fnpData.ibm3270ctlr[ASSUME0].stations[p->stationNo].write_complete = true;
1334     fnpData.ibm3270ctlr[ASSUME0].write_complete = true;
1335   }
1336 
1337 static void send_3270_msg (uint ctlr_no, unsigned char * msg, size_t len, bool brk)
     /* [previous][next][first][last][top][bottom][index][help] */
1338   {
1339 
1340 
1341 
1342 
1343 
1344 
1345 
1346 
1347     uint fnpno            = fnpData.ibm3270ctlr[ctlr_no].fnpno;
1348     uint lineno           = fnpData.ibm3270ctlr[ctlr_no].lineno;
1349     struct t_line * linep = & fnpData.fnpUnitData[fnpno].MState.line[lineno];
1350     if ((unsigned long) linep->nPos + len > sizeof (linep->buffer))
1351       sim_warn ("send_3270_msg overfull linep->buffer; dropping data\r\n");
1352     else
1353       {
1354         memcpy (linep->buffer + linep->nPos, msg, len);
1355         linep->nPos += len;
1356       }
1357 
1358 
1359 
1360 
1361 
1362 
1363 
1364     linep->force_accept_input = true;
1365     linep->accept_input       = 1;
1366     linep->input_break        = brk ? 1 : 0;
1367   }
1368 
1369 const unsigned char addr_map [ADDR_MAP_ENTRIES] =
1370   {
1371     0x40, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
1372     0xc8, 0xc9, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
1373     0x50, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
1374     0xd8, 0xd9, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f
1375   };
1376 
1377 static void send_stn_in_buffer (void)
     /* [previous][next][first][last][top][bottom][index][help] */
1378   {
1379 #if defined(TESTING)
1380       cpu_state_t * cpup = _cpup;
1381       sim_debug (DBG_TRACE, & fnp_dev, "fnp2 send_stn_in_buffer\r\n");
1382 #endif
1383 
1384 //dcl  1 text_msg unal based (textp),                         /* Format of normal text start */
1385 //       2 stx char (1),
1386 //       2 controller_address char (1),
1387 //       2 device_address char (1),
1388 //       2 aid char (1),                                      /* Reason for input (which key) */
1389 //       2 cursor1 char (1),
1390 //       2 cursor2 char (1);
1391 
1392 // ibm3270_mpx expects: STX text_msg ETX
1393 //
1394 // x3270 sends aid, cursor1, cursor2
1395 
1396 //sim_printf ("sending rcvd data\r\n");
1397 
1398     uint fnpno = fnpData.ibm3270ctlr[ASSUME0].fnpno;
1399     uint lineno = fnpData.ibm3270ctlr[ASSUME0].lineno;
1400     struct t_line * linep = & fnpData.fnpUnitData[fnpno].MState.line[lineno];
1401 
1402     // Idle until buffer available
1403     if (linep->accept_input)
1404       return;
1405     if (linep->input_reply_pending)
1406       return;
1407 
1408     struct ibm3270ctlr_s * ctlrp = & fnpData.ibm3270ctlr[ASSUME0];
1409     struct station_s * stnp = & fnpData.ibm3270ctlr[ASSUME0].stations[ctlrp->stn_no];
1410 
1411     uint left = linep->sync_msg_size;
1412 
1413     unsigned char * bufp = linep->buffer;
1414 
1415     * bufp ++  = 0x2; // STX
1416     left --;
1417 
1418     if (! stnp->hdr_sent)
1419       {
1420         * bufp ++  = addr_map [ASSUME0]; // Controller address
1421         left --;
1422         * bufp ++  = addr_map [ctlrp->stn_no]; // Station address
1423         left --;
1424         stnp->hdr_sent = true;
1425       }
1426 
1427     uint n_to_send = stnp->stn_in_size - stnp->stn_in_used;
1428     if (n_to_send > left)
1429       n_to_send = left;
1430     if (n_to_send)
1431       {
1432         sim_debug (DBG_TRACE, & fnp_dev, "handling in used %u %u\r\n", stnp->stn_in_used, n_to_send);
1433         //send_3270_msg (ASSUME0, stnp->stn_in_buffer + stnp->stn_in_used, n_to_send, false);
1434         //return;
1435         memcpy (bufp, stnp->stn_in_buffer + stnp->stn_in_used, n_to_send);
1436         bufp += n_to_send;
1437         stnp->stn_in_used += n_to_send;
1438         left -= n_to_send;
1439       }
1440 
1441     if (stnp->stn_in_used >= stnp->stn_in_size && left)
1442       {
1443         * bufp ++ = 0x3; // ETX
1444         left --;
1445 
1446         FREE (stnp->stn_in_buffer);
1447         stnp->stn_in_buffer = NULL;
1448         stnp->stn_in_size   = 0;
1449         stnp->stn_in_used   = 0;
1450 
1451         linep->input_break  = 1;
1452         fnpData.ibm3270ctlr[ASSUME0].sending_stn_in_buffer = false;
1453         //unsigned char ETX = 0x3;
1454         //send_3270_msg (ASSUME0, & ETX, sizeof (ETX), true);
1455       }
1456     uint sz = (uint) (bufp - linep->buffer);
1457     if (sz)
1458       {
1459         linep->force_accept_input = true;
1460         linep->accept_input       = 1;
1461         linep->nPos               = sz;
1462       }
1463 
1464 
1465 
1466 
1467 
1468 
1469   }
1470 
1471 static void fnp_process_3270_event (void)
     /* [previous][next][first][last][top][bottom][index][help] */
1472   {
1473 #if defined(TESTING)
1474     cpu_state_t * cpup    = _cpup;
1475 #endif
1476     uint fnpno            = fnpData.ibm3270ctlr[ASSUME0].fnpno;
1477     uint lineno           = fnpData.ibm3270ctlr[ASSUME0].lineno;
1478     struct t_line * linep = & fnpData.fnpUnitData[fnpno].MState.line[lineno];
1479 
1480 // Non-polling events
1481 
1482     if (fnpData.ibm3270ctlr[ASSUME0].sending_stn_in_buffer)
1483       {
1484         send_stn_in_buffer ();
1485         return;
1486       }
1487 
1488     if (fnpData.ibm3270ctlr[ASSUME0].write_complete)
1489       {
1490         fnpData.ibm3270ctlr[ASSUME0].write_complete = false;
1491         linep->lineStatus0                          = 6llu << 18; // IBM3270_WRITE_COMPLETE
1492         linep->lineStatus1                          = 0;
1493         linep->sendLineStatus                       = true;
1494       }
1495 
1496 // Polling events
1497 
1498     if (! fnpData.du3270_poll)
1499      return;
1500     fnpData.du3270_poll --;
1501     if (fnpData.du3270_poll)
1502       return;
1503     struct ibm3270ctlr_s * ctlrp = & fnpData.ibm3270ctlr[ASSUME0];
1504 
1505     sim_debug (DBG_TRACE, & fnp_dev, "fnp2 3270 poll\r\n");
1506     //uint fnpno = fnpData.ibm3270ctlr[ASSUME0].fnpno;
1507     //uint lineno = fnpData.ibm3270ctlr[ASSUME0].lineno;
1508     //struct t_line * linep = & fnpData.fnpUnitData[fnpno].MState.line[lineno];
1509     //linep->lineStatus0 = 0;
1510     //linep->lineStatus1 = 0;
1511     if (ctlrp->pollDevChar == 127) // General poll
1512       {
1513         uint stn_cnt;
1514         for (stn_cnt = 0; stn_cnt < IBM3270_STATIONS_MAX; stn_cnt ++)
1515           {
1516             ctlrp->stn_no           = (ctlrp->stn_no + 1) % IBM3270_STATIONS_MAX;
1517             struct station_s * stnp = & fnpData.ibm3270ctlr[ASSUME0].stations[ctlrp->stn_no];
1518             if (! stnp->client)
1519               continue;
1520             if (stnp->EORReceived)
1521               {
1522                 stnp->EORReceived            = false;
1523                 ctlrp->sending_stn_in_buffer = true;
1524                 fnpuv3270Poll (false);
1525                 break;
1526               }
1527           }
1528         if (stn_cnt >= IBM3270_STATIONS_MAX)
1529           {
1530             // No response to poll; send EOT, stop polling
1531 
1532             unsigned char EOT = 0x37;
1533             send_3270_msg (ASSUME0, & EOT, 1, true);
1534             fnpuv3270Poll (false);
1535           }
1536       }
1537     else
1538       {
1539         // Specific poll
1540         sim_debug (DBG_TRACE, & fnp_dev, "fnp2 specific poll\r\n");
1541       }
1542   }
1543 
1544 //
1545 // Called @ 100Hz to process FNP background events
1546 //
1547 
1548 void fnpProcessEvent (void)
     /* [previous][next][first][last][top][bottom][index][help] */
1549   {
1550 #if defined(TESTING)
1551     cpu_state_t * cpup = _cpup;
1552 #endif
1553     // Run the libuv event loop once.
1554     // Handles tcp connections, drops, read data, write data done.
1555     fnpuvProcessEvent ();
1556 
1557     // Move characters from inBuffer to buffer, based on line discipline
1558     // and data availability
1559 
1560     fnpProcessBuffers ();
1561 
1562     // Look for posted requests
1563     uint numunits = (uint) fnp_dev.numunits;
1564     for (uint fnp_unit_idx = 0; fnp_unit_idx < numunits; fnp_unit_idx ++)
1565       {
1566         if (! fnpData.fnpUnitData[fnp_unit_idx].fnpIsRunning)
1567           continue;
1568         int mbx = findMbx (fnp_unit_idx);
1569         if (mbx == -1)
1570           continue;
1571         bool need_intr = false;
1572         for (int lineno = 0; lineno < MAX_LINES; lineno ++)
1573           {
1574             struct t_line * linep = & fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno];
1575 
1576 #if defined(DISC_DELAY)
1577             // Disconnect pending?
1578             if (linep -> line_disconnected > 1)
1579               {
1580                 // Buffer not empty?
1581                 if (linep->inBuffer && linep->inUsed < linep->inSize)
1582                   {
1583                      // Reset timer
1584                      linep -> line_disconnected = DISC_DELAY;
1585                   }
1586                 else
1587                   {
1588                     // Decrement timer
1589                     -- linep -> line_disconnected;
1590                   }
1591               }
1592 #endif /* if defined(DISC_DELAY) */
1593 
1594             // Are we waiting for the previous command to complete?
1595             if (linep->waitForMbxDone)
1596               continue;
1597 
1598             // Need to send a 'send_output' command to CS?
1599 
1600             bool do_send_output = linep->send_output == 1;
1601 
1602             if (linep -> send_output > 0)
1603                 linep->send_output --;
1604 
1605             if (do_send_output)
1606               {
1607                 fnp_rcd_send_output ((uint)mbx, (int) fnp_unit_idx, lineno);
1608                 need_intr = true;
1609               }
1610 
1611             // Need to send an 'acu_dial_failure' command to CS?
1612 
1613             else if (linep->acu_dial_failure)
1614               {
1615                 fnp_rcd_acu_dial_failure ((uint)mbx, (int) fnp_unit_idx, lineno);
1616                 linep->acu_dial_failure = false;
1617                 need_intr               = true;
1618               }
1619 
1620             // Need to send an 'accept_new_terminal' command to CS?
1621 
1622 // linep->listen is check here for the case of a connection that was
1623 // made before Multics was booted to the point of setting listen.
1624 // If the accept_new_terminal call is made then, Multics rejects
1625 // the connection and sends a disconnect order. By checking 'listen',
1626 // the accept requests hangs around until Multics is ready.
1627 
1628             else if (linep->listen && linep->accept_new_terminal)
1629               {
1630                 fnp_rcd_accept_new_terminal ((uint)mbx, (int) fnp_unit_idx, lineno);
1631                 linep->accept_new_terminal = false;
1632                 need_intr                  = true;
1633               }
1634 
1635             // Need to send an 'ack_echnego_init' command to CS?
1636 
1637             else if (linep -> ack_echnego_init)
1638               {
1639                 fnp_rcd_ack_echnego_init ((uint)mbx, (int) fnp_unit_idx, lineno);
1640                 linep -> ack_echnego_init = false;
1641                 linep -> send_output      = SEND_OUTPUT_DELAY;
1642                 need_intr                 = true;
1643               }
1644 
1645             // Need to send an 'ack_echnego_stop' command to CS?
1646 
1647             else if (linep -> ack_echnego_stop)
1648               {
1649                 fnp_rcd_ack_echnego_stop ((uint)mbx, (int) fnp_unit_idx, lineno);
1650                 linep -> ack_echnego_stop = false;
1651                 linep -> send_output      = SEND_OUTPUT_DELAY;
1652                 need_intr                 = true;
1653               }
1654 
1655             // Need to send an 'line_disconnected' command to CS?
1656 
1657 #if defined(DISC_DELAY)
1658             else if (linep -> line_disconnected == 1)
1659               {
1660                 fnp_rcd_line_disconnected ((uint)mbx, (int) fnp_unit_idx, lineno);
1661                 linep -> line_disconnected = 0;
1662                 linep -> listen            = false;
1663                 need_intr                  = true;
1664               }
1665 #else
1666             else if (linep -> line_disconnected)
1667               {
1668                 fnp_rcd_line_disconnected ((uint)mbx, (int) fnp_unit_idx, lineno);
1669                 linep -> line_disconnected = false;
1670                 linep -> listen            = false;
1671                 need_intr                  = true;
1672               }
1673 #endif /* if defined(DISC_DELAY) */
1674 
1675             // Need to send an 'wru_timeout' command to CS?
1676 
1677             else if (linep -> wru_timeout)
1678               {
1679                 fnp_rcd_wru_timeout ((uint)mbx, (int) fnp_unit_idx, lineno);
1680                 linep -> wru_timeout = false;
1681                 need_intr            = true;
1682               }
1683 
1684             // Need to send an 'accept_input' or 'input_in_mailbox' command to CS?
1685 
1686             else if (linep->accept_input && ! linep->waitForMbxDone) //-V560
1687               {
1688                 if (linep->accept_input == 1)
1689                   {
1690                     // This check was added as part of 3270 support,
1691                     // but breaks break key logic. Disabling until
1692                     // a case can be made that the 3270 requires this.
1693                     /* LINTED E_FALSE_LOGICAL_EXPR*/
1694                     if (0 && linep->nPos == 0)
1695                       {
1696                         sim_printf ("dropping nPos of 0");
1697                       }
1698                     else
1699                       {
1700                         //sim_printf ("\r\n nPos %d\r\n", linep->nPos);
1701 
1702 
1703 
1704 
1705 
1706 
1707 
1708 
1709 
1710 
1711 
1712 
1713 
1714 
1715 
1716 
1717 // There is a bufferfull of data that needs to be sent to the CS.
1718 // If the buffer has < 101 characters, use the 'input_in_mailbox'
1719 // command; otherwise use the 'accept_input/input_accepted'
1720 // sequence.
1721 
1722 
1723 
1724 
1725 
1726 
1727 
1728                         if (linep->force_accept_input || linep->nPos > 100)
1729                           {
1730                             fnp_rcd_accept_input ((uint)mbx, (int) fnp_unit_idx, lineno);
1731                             //linep->input_break = false;
1732                             linep->input_reply_pending = true;
1733                             // accept_input cleared below
1734                             need_intr = true;
1735                           }
1736                         else
1737                           {
1738                             fnp_rcd_input_in_mailbox ((uint)mbx, (int) fnp_unit_idx, lineno);
1739                             sim_debug (DBG_TRACE, & fnp_dev, "FNP input_in_mailbox\r\n");
1740                             linep->nPos = 0;
1741                             // accept_input cleared below
1742                             need_intr = true;
1743                           }
1744 
1745                       }
1746                   }
1747                 linep->accept_input --;
1748               } // accept_input
1749 
1750             // Need to send a 'line_break' command to CS?
1751             // This goes after the accept_input to that when BREAK occurs,
1752             // first the input is flushed and then the break signal is sent.
1753 
1754             else if (linep->line_break)
1755               {
1756                 fnp_rcd_line_break ((uint)mbx, (int) fnp_unit_idx, lineno);
1757                 linep -> line_break  = false;
1758                 need_intr            = true;
1759                 linep -> send_output = SEND_OUTPUT_DELAY;
1760               }
1761 
1762             else if (linep->sendLineStatus)
1763               {
1764                 linep->sendLineStatus = false;
1765                 fnp_rcd_line_status ((uint)mbx, (int) fnp_unit_idx, lineno);
1766                 need_intr             = true;
1767               }
1768 
1769             else
1770               {
1771                 continue;
1772               }
1773 
1774             // One of the request processes may have consumed the
1775             // mailbox; make sure one is still available
1776 
1777             mbx = findMbx (fnp_unit_idx);
1778             if (mbx == -1)
1779               break;
1780           } // for lineno
1781 
1782         // If any of the mailboxes had a command posted.
1783         if (need_intr)
1784           {
1785             uint ctlr_port_num = 0; // FNPs are single ported
1786             uint iom_unit_idx  = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].iom_unit_idx;
1787             uint chan_num      = cables->fnp_to_iom[fnp_unit_idx][ctlr_port_num].chan_num;
1788             send_general_interrupt (iom_unit_idx, chan_num, imwTerminatePic);
1789           }
1790       } // for fnp_unit_idx
1791 
1792 #if defined(TUN)
1793     fnpTUNProcessEvent ();
1794 #endif /* if defined(TUN) */
1795     fnp_process_3270_event ();
1796   }
1797 
1798 static t_stat fnpShowNUnits (UNUSED FILE * st, UNUSED UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
1799                              UNUSED int val, UNUSED const void * desc)
1800   {
1801     sim_printf("Number of FNP units in system is %d\r\n", fnp_dev . numunits);
1802     return SCPE_OK;
1803   }
1804 
1805 static t_stat fnpSetNUnits (UNUSED UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
1806                             const char * cptr, UNUSED void * desc)
1807   {
1808     if (! cptr)
1809       return SCPE_ARG;
1810     int n = atoi (cptr);
1811     if (n < 1 || n > N_FNP_UNITS_MAX)
1812       return SCPE_ARG;
1813     fnp_dev . numunits = (uint32) n;
1814     //return fnppSetNunits (uptr, value, cptr, desc);
1815     return SCPE_OK;
1816   }
1817 
1818 static t_stat fnpShowIPCname (UNUSED FILE * st, UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
1819                               UNUSED int val, UNUSED const void * desc)
1820   {
1821     long n = FNP_UNIT_IDX (uptr);
1822     if (n < 0 || n >= N_FNP_UNITS_MAX)
1823       return SCPE_ARG;
1824     sim_printf(" FNP IPC name: %s", fnpData.fnpUnitData [n] . ipcName);
1825     return SCPE_OK;
1826   }
1827 
1828 static t_stat fnpSetIPCname (UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
1829                              UNUSED const char * cptr, UNUSED void * desc)
1830   {
1831     long n = FNP_UNIT_IDX (uptr);
1832     if (n < 0 || n >= N_FNP_UNITS_MAX)
1833       return SCPE_ARG;
1834     if (cptr)
1835       {
1836         strncpy (fnpData.fnpUnitData [n] . ipcName, cptr, MAX_DEV_NAME_LEN - 1);
1837         fnpData.fnpUnitData [n] . ipcName [MAX_DEV_NAME_LEN - 1] = 0;
1838       }
1839     else
1840       fnpData.fnpUnitData [n] . ipcName [0] = 0;
1841     return SCPE_OK;
1842   }
1843 
1844 static t_stat fnpShowService (UNUSED FILE * st, UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
1845                               UNUSED int val, UNUSED const void * desc)
1846   {
1847     long devnum = FNP_UNIT_IDX (uptr);
1848     if (devnum < 0 || devnum >= N_FNP_UNITS_MAX)
1849       return SCPE_ARG;
1850     for (uint linenum = 0; linenum < MAX_LINES; linenum ++)
1851       {
1852         if (linenum == 0)
1853             sim_printf("\t");
1854         else
1855             sim_printf("\t\t");
1856         enum service_types st = fnpData.fnpUnitData[devnum].MState.line[linenum].service;
1857         switch (st)
1858           {
1859             case service_undefined:
1860               sim_printf("%c.%03d: undefined", (char)('a' + (int) devnum), linenum);
1861               break;
1862             case service_login:
1863               sim_printf("%c.%03d: login",     (char)('a' + (int) devnum), linenum);
1864               break;
1865             case service_autocall:
1866               sim_printf("%c.%03d: autocall",  (char)('a' + (int) devnum), linenum);
1867               break;
1868             case service_slave:
1869               sim_printf("%c.%03d: slave",     (char)('a' + (int) devnum), linenum);
1870               break;
1871             default:
1872               sim_printf("%d.%03d: ERR (%u)",  (char)('a' + (int) devnum), linenum, st);
1873               break;
1874           }
1875         if (linenum != (MAX_LINES - 1))
1876             sim_printf("\r\n");
1877       }
1878     return SCPE_OK;
1879   }
1880 
1881 static t_stat fnpSetService (UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
1882                              const char * cptr, UNUSED void * desc)
1883   {
1884     if (! cptr)
1885       return SCPE_ARG;
1886     long devnum = FNP_UNIT_IDX (uptr);
1887     if (devnum < 0 || devnum >= N_FNP_UNITS_MAX)
1888       return SCPE_ARG;
1889     // set fnp3 service=30=autocall
1890     // set fnp3 service=31=slave
1891     uint linenum;
1892     char sn [strlen (cptr) + 1];
1893     int nr = sscanf (cptr, "%u=%s", & linenum, sn);
1894     if (nr != 2)
1895       return SCPE_ARG;
1896     if (linenum >= MAX_LINES)
1897       return SCPE_ARG;
1898     if (strcasecmp (sn, "undefined") == 0)
1899       fnpData.fnpUnitData[devnum].MState.line[linenum].service = service_undefined;
1900     else if (strcasecmp (sn, "login") == 0)
1901       fnpData.fnpUnitData[devnum].MState.line[linenum].service = service_login;
1902     else if (strcmp (sn, "ibm3270") == 0)
1903       {
1904         fnpData.fnpUnitData[devnum].MState.line[linenum].service = service_3270;
1905         fnpData.ibm3270ctlr[ASSUME0].fnpno                       = (uint) devnum;
1906         fnpData.ibm3270ctlr[ASSUME0].lineno                      = linenum;
1907       }
1908     else if (strcasecmp (sn, "autocall") == 0)
1909       fnpData.fnpUnitData[devnum].MState.line[linenum].service = service_autocall;
1910     else if (strncasecmp (sn, "slave=", 6) == 0)
1911       {
1912         uint pn;
1913         int nr2 = sscanf (sn, "slave=%u", & pn);
1914         if (nr2 != 1)
1915           return SCPE_ARG;
1916         if (pn >= 65535)
1917           return SCPE_ARG;
1918         fnpData.fnpUnitData[devnum].MState.line[linenum].service = service_slave;
1919         fnpData.fnpUnitData[devnum].MState.line[linenum].port    = (int) pn;
1920       }
1921     else
1922       return SCPE_ARG;
1923     return SCPE_OK;
1924   }
1925 
1926 static t_stat fnpShowConfig (UNUSED FILE * st, UNIT * uptr, UNUSED int val,
     /* [previous][next][first][last][top][bottom][index][help] */
1927                              UNUSED const void * desc)
1928   {
1929 #if defined(TESTING)
1930     cpu_state_t * cpup = _cpup;
1931 #endif
1932     long fnpUnitIdx = FNP_UNIT_IDX (uptr);
1933     if (fnpUnitIdx >= (long) N_FNP_UNITS_MAX)
1934       {
1935         sim_debug (DBG_ERR, & fnp_dev,
1936                    "fnpShowConfig: Invalid unit number %ld\r\n", (long) fnpUnitIdx);
1937         sim_printf ("error: Invalid unit number %ld\r\n", (long) fnpUnitIdx);
1938         return SCPE_ARG;
1939       }
1940 
1941     sim_printf ("FNP unit number %ld\r\n", (long) fnpUnitIdx);
1942     struct fnpUnitData_s * fudp = fnpData.fnpUnitData + fnpUnitIdx;
1943 
1944     sim_printf ("FNP mailbox address:         %04o(8)\r\n", fudp -> mailboxAddress);
1945 
1946     return SCPE_OK;
1947   }
1948 
1949 //  SET FNPn FW RESET
1950 //  SET FNPn FW ADD <line number list>:<ipaddr>:<ipmask>: ACCEPT | DENY
1951 
1952 int n_fw_entries = 0;
1953 struct fw_entry_s fw_entries [N_FW_ENTRIES];
1954 
1955 // [a-h].h[0-9][0-9][0-9]
1956 // terminated by dash or NUL
1957 
1958 static int parse_line (char * line)
     /* [previous][next][first][last][top][bottom][index][help] */
1959   {
1960     char fnp = line[0];
1961     if (fnp < 'A' || fnp > 'H')
1962       return -1;
1963     int fnpno = fnp - 'A';
1964 
1965     if (line [1] != '.')
1966       return -2;
1967 
1968     if (line[2] != 'H')
1969       return -3;
1970 
1971     if (line[3] < '0' || line[3] > '9')
1972       return -4;
1973 
1974     if (line[4] < '0' || line[4] > '9')
1975       return -5;
1976 
1977     if (line[5] < '0' || line[5] > '9')
1978       return -6;
1979     int lineno = (line[3] - '0') * 100 +
1980                  (line[4] - '0') * 10 +
1981                  (line[5] - '0');
1982 
1983     if (line[6] != 0 && line[6] != '-')
1984       return -7;
1985 
1986     return encodeline (fnpno, lineno);
1987 
1988   }
1989 
1990 // n.n.n.n
1991 static int parse_ipaddr (char * str, uint32_t * addr)
     /* [previous][next][first][last][top][bottom][index][help] */
1992   {
1993     char * end1, * end2, * end3, * end4;
1994 
1995     unsigned long o1 = strtoul (str, & end1, 10);
1996     if (end1 == str  || * end1 != '.' || o1 > 255)
1997       return -1;
1998 
1999     unsigned long o2 = strtoul (end1 + 1, & end2, 10);
2000     if (end2 == end1 || * end2 != '.' || o2 > 255)
2001       return -2;
2002 
2003     unsigned long o3 = strtoul (end2 + 1, & end3, 10);
2004     if (end3 == end2 || * end3 != '.' || o3 > 255)
2005       return -3;
2006 
2007     unsigned long o4 = strtoul (end3 + 1, & end4, 10);
2008     if (end4 == end3 || * end4 != 0   || o4 > 255)
2009       return -4;
2010     * addr = (uint32_t) ((o1 << 24) | (o2 << 16) | (o3 << 8) | o4);
2011     return 0;
2012   }
2013 
2014 static t_stat fnpSetFW (UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
2015                         const char * cptr, UNUSED void * desc)
2016   {
2017     if (! cptr)
2018       return SCPE_ARG;
2019     long devnum = FNP_UNIT_IDX (uptr);
2020     if (devnum < 0 || devnum >= N_FNP_UNITS_MAX)
2021       return SCPE_ARG;
2022 
2023     char sn [strlen (cptr) + 1];
2024     memcpy (sn, cptr, strlen (cptr) + 1);
2025     char * saveptr;
2026     char * tok;
2027 
2028 // Parse out ADD/RESET
2029     tok = strtok_r (sn, ":", & saveptr);
2030     if (strcasecmp (tok, "RESET") == 0)
2031       {
2032         n_fw_entries = 0;
2033         sim_printf ("FNP firewall table reset\r\n");
2034         return SCPE_OK;
2035       }
2036 
2037     if (strcasecmp (tok, "ADD") == 0)
2038       {
2039         if (n_fw_entries >= N_FW_ENTRIES)
2040           {
2041             sim_printf ("FNP firewall table full\r\n");
2042             return SCPE_ARG;
2043           }
2044         // line range
2045         int line_0, line_1;
2046 
2047         tok = strtok_r (NULL, ":", & saveptr);
2048         char * dash = strchr (tok, '-');
2049         if (dash)
2050           {
2051             line_0 = parse_line (tok);
2052             if (line_0 < 0)
2053               {
2054                 sim_printf ("Cannot parse first line number\r\n");
2055                 return SCPE_ARG;
2056               }
2057 
2058             line_1 = parse_line (dash + 1);
2059             if (line_1 < 0)
2060               {
2061                 sim_printf ("Cannot parse second line number\r\n");
2062                 return SCPE_ARG;
2063               }
2064             if (line_0 > line_1)
2065               {
2066                 sim_printf ("line_0 > line_1\r\n");
2067                 return SCPE_ARG;
2068               }
2069 
2070           }
2071         else
2072           {
2073             line_0 = line_1 = parse_line (tok);
2074             if (line_0 < 0)
2075               {
2076                 sim_printf ("Cannot parse line number\r\n");
2077                 return SCPE_ARG;
2078               }
2079           }
2080 
2081 // parse ipaddr
2082 
2083         tok    = strtok_r (NULL, ":", & saveptr);
2084         uint32_t ipaddr;
2085         int rc = parse_ipaddr (tok, & ipaddr);
2086         if (rc < 0)
2087           return SCPE_ARG;
2088 
2089 // parse cidr
2090 
2091         tok = strtok_r (NULL, ":", & saveptr);
2092         char * end;
2093         unsigned long cidr = strtoul (tok, & end, 10);
2094         if (tok == end || * end != 0 || cidr > 32)
2095           return SCPE_OK;
2096         uint32_t cidr_mask = (cidr == 0) ? 0 :
2097           ((uint32_t)-1 << (32 - cidr)) & MASK32;
2098 
2099 // parse accept/deny
2100 
2101         bool accept = false;
2102         tok         = strtok_r (NULL, ":", & saveptr);
2103         if (strcmp (tok, "ACCEPT") == 0)
2104           accept = true;
2105         else if (strcmp (tok, "DENY") == 0)
2106           accept = false;
2107         else
2108           {
2109             sim_printf ("cannot parse rule ACCEPT/DENY\r\n");
2110             return SCPE_ARG;
2111           }
2112 
2113         fw_entries[n_fw_entries].line_0    = (uint) line_0;
2114         fw_entries[n_fw_entries].line_1    = (uint) line_1;
2115         fw_entries[n_fw_entries].ipaddr    = ipaddr;
2116         fw_entries[n_fw_entries].cidr      = (uint) cidr;
2117         fw_entries[n_fw_entries].cidr_mask = (uint) cidr_mask;
2118         fw_entries[n_fw_entries].accept    = accept;
2119         n_fw_entries ++;
2120 
2121         return SCPE_OK;
2122       } // ADD
2123 
2124     if (strcasecmp (tok, "LIST") == 0)
2125       {
2126         for (int i = 0; i < n_fw_entries; i ++)
2127           {
2128             struct fw_entry_s * p = fw_entries + i;
2129 
2130             if (p->line_0 == p->line_1)
2131               {
2132                 sim_printf ("  %c.h%03d %d.%d.%d.%d/%d %s\r\n",
2133                   decodefnp  (p->line_0) + 'a',
2134                   decodeline (p->line_0),
2135                   (p->ipaddr>>24)  & 255,
2136                   (p->ipaddr>>16)  & 255,
2137                   (p->ipaddr>>8)   & 255,
2138                   p->ipaddr        & 255,
2139                   p->cidr,
2140                   p->accept ? "accept" : "deny");
2141               }
2142             else
2143               {
2144                 sim_printf ("  %c.h%03d-%c.%03d %d.%d.%d.%d/%d %s\r\n",
2145                   decodefnp  (p->line_0) + 'a',
2146                   decodeline (p->line_0),
2147                   decodefnp  (p->line_1) + 'a',
2148                   decodeline (p->line_1),
2149                   (p->ipaddr>>24)  & 255,
2150                   (p->ipaddr>>16)  & 255,
2151                   (p->ipaddr>>8)   & 255,
2152                   p->ipaddr        & 255,
2153                   p->cidr,
2154                   p->accept ? "accept" : "deny");
2155               }
2156           }
2157        return SCPE_OK;
2158       }
2159 
2160     return SCPE_ARG;
2161   }
2162 
2163 static t_stat fnpShowFW (UNUSED FILE * st, UNIT * uptr, UNUSED int val,
     /* [previous][next][first][last][top][bottom][index][help] */
2164                          UNUSED const void * desc)
2165   {
2166 #if defined(TESTING)
2167     cpu_state_t * cpup = _cpup;
2168 #endif
2169     long fnpUnitIdx = FNP_UNIT_IDX (uptr);
2170     if (fnpUnitIdx >= (long) N_FNP_UNITS_MAX)
2171       {
2172         sim_debug (DBG_ERR, & fnp_dev,
2173                    "fnpShowConfig: Invalid unit number %ld\r\n",
2174                    (long) fnpUnitIdx);
2175         sim_printf ("error: Invalid unit number %ld\r\n",
2176                     (long) fnpUnitIdx);
2177         return SCPE_ARG;
2178       }
2179 
2180 
2181 
2182 
2183 
2184 
2185 
2186     return SCPE_OK;
2187   }
2188 
2189 static t_stat fnpShowStatus (UNUSED FILE * st, UNIT * uptr, UNUSED int val,
     /* [previous][next][first][last][top][bottom][index][help] */
2190                              UNUSED const void * desc)
2191   {
2192 #if defined(TESTING)
2193     cpu_state_t * cpup = _cpup;
2194 #endif
2195     long fnpUnitIdx = FNP_UNIT_IDX (uptr);
2196     if (fnpUnitIdx >= (long) fnp_dev.numunits)
2197       {
2198         sim_debug (DBG_ERR, & fnp_dev,
2199                    "fnpShowStatus: Invalid unit number %ld\r\n",
2200                    (long) fnpUnitIdx);
2201         sim_printf ("error: Invalid unit number %ld\r\n",
2202                     (long) fnpUnitIdx);
2203         return SCPE_ARG;
2204       }
2205 
2206     sim_printf ("FNP unit number %ld:\r\n", (long) fnpUnitIdx);
2207     struct fnpUnitData_s * fudp = fnpData.fnpUnitData + fnpUnitIdx;
2208 
2209     sim_printf ("\tmailboxAddress:              %04o\r\n",
2210                 fudp->mailboxAddress);
2211     sim_printf ("\tfnpIsRunning:                %o\r\n",
2212                 fudp->fnpIsRunning);
2213     sim_printf ("\tfnpMBXinUse:                 %o %o %o %o\r\n",
2214                 fudp->fnpMBXinUse[0], fudp->fnpMBXinUse[1],
2215                 fudp->fnpMBXinUse[2], fudp->fnpMBXinUse[3]);
2216     sim_printf ("\tlineWaiting:                 %o %o %o %o\r\n",
2217                 fudp->lineWaiting[0], fudp->lineWaiting[1],
2218                 fudp->lineWaiting[2], fudp->lineWaiting[3]);
2219     sim_printf ("\tfnpMBXlineno:                %o %o %o %o\r\n",
2220                 fudp->fnpMBXlineno[0], fudp->fnpMBXlineno[1],
2221                 fudp->fnpMBXlineno[2], fudp->fnpMBXlineno[3]);
2222     sim_printf ("\taccept_calls:                %o\r\n",
2223                 fudp->MState.accept_calls);
2224     for (int l = 0; l < MAX_LINES; l ++)
2225       {
2226         sim_printf ("  line %d:\r\n", l);
2227         sim_printf ("\tservice:                     %d\r\n",
2228                     fudp->MState.line[l].service);
2229         sim_printf ("\tline_client:                 %p\r\n",
2230                     (void *) fudp->MState.line[l].line_client);
2231         sim_printf ("\twas_CR:                      %d\r\n",
2232                     fudp->MState.line[l].was_CR);
2233         sim_printf ("\tlisten:                      %d\r\n",
2234                     fudp->MState.line[l].listen);
2235         sim_printf ("\tinputBufferSize:             %d\r\n",
2236                     fudp->MState.line[l].inputBufferSize);
2237         sim_printf ("\tline_break:                  %d\r\n",
2238                     fudp->MState.line[l].line_break);
2239         sim_printf ("\tsend_output:                 %d\r\n",
2240                     fudp->MState.line[l].send_output);
2241         sim_printf ("\taccept_new_terminal:         %d\r\n",
2242                     fudp->MState.line[l].accept_new_terminal);
2243 #if DISC_DELAY
2244         sim_printf ("\tline_disconnected:           %d\r\n",
2245                     fudp->MState.line[l].line_disconnected);
2246 #else
2247         sim_printf ("\tline_disconnected:           %c\r\n",
2248                     fudp->MState.line[l].line_disconnected ? 'T' : 'F');
2249 #endif
2250         sim_printf ("\tacu_dial_failure:            %d\r\n",
2251                     fudp->MState.line[l].acu_dial_failure);
2252         sim_printf ("\taccept_input:                %d\r\n",
2253                     fudp->MState.line[l].accept_input);
2254         sim_printf ("\twaitForMbxDone:              %d\r\n",
2255                     fudp->MState.line[l].waitForMbxDone);
2256         sim_printf ("\tinput_reply_pending:         %d\r\n",
2257                     fudp->MState.line[l].input_reply_pending);
2258         sim_printf ("\tinput_break:                 %d\r\n",
2259                     fudp->MState.line[l].input_break);
2260         sim_printf ("\tnPos:                        %d\r\n",
2261                     fudp->MState.line[l].nPos);
2262         sim_printf ("\tinBuffer:                    %p\r\n",
2263                     (void *) fudp->MState.line[l].inBuffer);
2264         sim_printf ("\tinSize:                      %d\r\n",
2265                     fudp->MState.line[l].inSize);
2266         sim_printf ("\tinUsed:                      %d\r\n",
2267                     fudp->MState.line[l].inUsed);
2268         //sim_printf ("\tdoConnect:                   %p\r\n",
2269         //            fudp->MState.line[l].doConnect);
2270         //sim_printf ("\tserver:                      %p\r\n",
2271         //            fudp->MState.line[l].server);
2272         sim_printf ("\tport:                        %d\r\n",
2273                     fudp->MState.line[l].port);
2274 
2275       }
2276     return SCPE_OK;
2277   }
2278 
2279 static t_stat fnp_show_device_name (UNUSED FILE * st, UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
2280                                     UNUSED int val, UNUSED const void * desc)
2281   {
2282     int n = (int) FNP_UNIT_IDX (uptr);
2283     if (n < 0 || n >= N_FNP_UNITS_MAX)
2284       return SCPE_ARG;
2285     if (fnpData.fnpUnitData[n].device_name[0] != 0)
2286       sim_printf("         name: %s", fnpData.fnpUnitData[n].device_name);
2287     else
2288       sim_printf("         name: default");
2289     return SCPE_OK;
2290   }
2291 
2292 static t_stat fnp_set_device_name (UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
2293                                    const char * cptr, UNUSED void * desc)
2294   {
2295     int n = (int) FNP_UNIT_IDX (uptr);
2296     if (n < 0 || n >= N_FNP_UNITS_MAX)
2297       return SCPE_ARG;
2298     if (cptr)
2299       {
2300         strncpy (fnpData.fnpUnitData[n].device_name, cptr, MAX_DEV_NAME_LEN-1);
2301         fnpData.fnpUnitData[n].device_name[MAX_DEV_NAME_LEN-1] = 0;
2302       }
2303     else
2304       fnpData.fnpUnitData[n].device_name[0] = 0;
2305     return SCPE_OK;
2306   }
2307 
2308 static config_list_t fnp_config_list [] =
2309   {
2310     /*  0 */ { "mailbox", 0, 07777, NULL },
2311     { NULL, 0, 0, NULL }
2312   };
2313 
2314 static t_stat fnpSetConfig (UNIT * uptr, UNUSED int value, const char * cptr, UNUSED void * desc)
     /* [previous][next][first][last][top][bottom][index][help] */
2315   {
2316 #if defined(TESTING)
2317     cpu_state_t * cpup = _cpup;
2318 #endif
2319     uint fnpUnitIdx = (uint) FNP_UNIT_IDX (uptr);
2320     //if (fnpUnitIdx >= fnp_dev . numunits)
2321     if (fnpUnitIdx >= N_FNP_UNITS_MAX)
2322       {
2323         sim_debug (DBG_ERR, & fnp_dev, "fnpSetConfig: Invalid unit number %ld\r\n", (long) fnpUnitIdx);
2324         sim_printf ("error: fnpSetConfig: Invalid unit number %ld\r\n", (long) fnpUnitIdx);
2325         return SCPE_ARG;
2326       }
2327 
2328     struct fnpUnitData_s * fudp = fnpData.fnpUnitData + fnpUnitIdx;
2329 
2330     config_state_t cfg_state = { NULL, NULL };
2331 
2332     for (;;)
2333       {
2334         int64_t v;
2335         int rc = cfg_parse ("fnpSetConfig", cptr, fnp_config_list, & cfg_state, & v);
2336         switch (rc)
2337           {
2338             case -2: // error
2339               cfg_parse_done (& cfg_state);
2340               return SCPE_ARG;
2341 
2342             case -1: // done
2343               break;
2344 
2345             case 0: // mailbox
2346               fudp -> mailboxAddress = (uint) v;
2347               break;
2348 
2349             default:
2350               sim_printf ("error: fnpSetConfig: Invalid cfg_parse rc <%ld>\r\n", (long) rc);
2351               cfg_parse_done (& cfg_state);
2352               return SCPE_ARG;
2353           } // switch
2354         if (rc < 0)
2355           break;
2356       } // process statements
2357     cfg_parse_done (& cfg_state);
2358     return SCPE_OK;
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 
2460 
2461 t_stat set_fnp_server_port (UNUSED int32 arg, const char * buf)
     /* [previous][next][first][last][top][bottom][index][help] */
2462   {
2463     if (fnpData.du_server_inited)
2464       {
2465         sim_printf ("[FNP emulation: TELNET server port error: FNP already initialized]\r\n");
2466         return SCPE_INCOMP;
2467       }
2468     if (! buf)
2469       return SCPE_ARG;
2470     int n = atoi (buf);
2471     if (n < 1 || n > 65535)
2472       return SCPE_ARG;
2473     fnpData.telnet_port = n;
2474         if (! sim_quiet)
2475           {
2476             sim_printf ("[FNP emulation: TELNET server port set to %ld]\r\n", (long) n);
2477           }
2478     return SCPE_OK;
2479   }
2480 
2481 t_stat set_fnp_server_address (UNUSED int32 arg, const char * buf)
     /* [previous][next][first][last][top][bottom][index][help] */
2482   {
2483     if (fnpData.du_server_inited)
2484       {
2485         sim_printf ("[FNP emulation: FNP server address error: FNP already initialized]\r\n");
2486         return SCPE_INCOMP;
2487       }
2488     if ( (!buf) || (buf[0] == 0) )
2489         return SCPE_ARG;
2490     if (fnpData.telnet_address)
2491       FREE (fnpData.telnet_address);
2492     fnpData.telnet_address = strdup (buf);
2493     if (!fnpData.telnet_address)
2494       {
2495         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2496                        __func__, __FILE__, __LINE__);
2497 #if defined(USE_BACKTRACE)
2498 # if defined(SIGUSR2)
2499         (void)raise(SIGUSR2);
2500         /*NOTREACHED*/ /* unreachable */
2501 # endif /* if defined(SIGUSR2) */
2502 #endif /* if defined(USE_BACKTRACE) */
2503         abort();
2504       }
2505         if (! sim_quiet)
2506           {
2507             sim_printf ("[FNP emulation: FNP server address set to %s]\r\n", fnpData.telnet_address);
2508           }
2509     return SCPE_OK;
2510   }
2511 
2512 t_stat set_fnp_3270_server_port (UNUSED int32 arg, const char * buf)
     /* [previous][next][first][last][top][bottom][index][help] */
2513   {
2514     /* Checking both here since there is no explicit FNP3270START */
2515     if ( (fnpData.du3270_server_inited) || (fnpData.du_server_inited) )
2516       {
2517         sim_printf ("[FNP emulation: TN3270 server port error: FNP already initialized]\r\n");
2518         return SCPE_INCOMP;
2519       }
2520     if (! buf)
2521       return SCPE_ARG;
2522     int n = atoi (buf);
2523     if (n < 1 || n > 65535)
2524       return SCPE_ARG;
2525     fnpData.telnet3270_port = n;
2526         if (! sim_quiet)
2527           {
2528             sim_printf ("[FNP emulation: TN3270 server port set to %ld]\r\n", (long) n);
2529           }
2530     return SCPE_OK;
2531   }
2532 
2533 t_stat fnp_start (UNUSED int32 arg, UNUSED const char * buf)
     /* [previous][next][first][last][top][bottom][index][help] */
2534   {
2535     int rc = 0;
2536     rc = fnpuvInit (fnpData.telnet_port, fnpData.telnet_address);
2537     if (rc != 0)
2538       return SCPE_INCOMP;
2539     // rc = fnpuv3270Init (fnpData.telnet3270_port);
2540     // if (rc != 0)
2541     //   return SCPE_INCOMP;
2542     return SCPE_OK;
2543   }
2544 
2545 #define PROMPT  "HSLA Port ("
2546 
2547 void fnpConnectPrompt (uv_tcp_t * client)
     /* [previous][next][first][last][top][bottom][index][help] */
2548   {
2549     fnpuv_start_writestr (client, (unsigned char *) PROMPT);
2550     bool first = true;
2551     uint numunits = (uint) fnp_dev.numunits;
2552     for (uint fnp_unit_idx = 0; fnp_unit_idx < numunits; fnp_unit_idx ++)
2553       {
2554         if (! fnpData.fnpUnitData[fnp_unit_idx].fnpIsRunning)
2555           continue;
2556         for (uint lineno = 0; lineno < MAX_LINES; lineno ++)
2557           {
2558             struct t_line * linep = & fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno];
2559             if (! linep->listen)
2560               continue;
2561             if (linep->service == service_login &&
2562                 ! linep->line_disconnected &&
2563                 ! linep->line_client &&
2564                 fnpData.fnpUnitData[fnp_unit_idx].MState.accept_calls)
2565               {
2566                 if (! first)
2567                   fnpuv_start_writestr (client, (unsigned char *) ",");
2568                 char name [16];
2569                 first = false;
2570                 (void)sprintf (name, "%c.h%03d", 'a' + fnp_unit_idx, lineno);
2571                 fnpuv_start_writestr (client, (unsigned char *) name);
2572               }
2573           }
2574       }
2575     fnpuv_start_writestr (client, (unsigned char *) ")? ");
2576   }
2577 
2578 /*
2579  * ASCII <=> EBCDIC conversion functions
2580  */
2581 
2582 const unsigned char a2e[256] = {
2583           0, 1,   2,   3,   55,  45,  46,  47,  22,  5,   37,  11,  12,  13,  14,  15,
2584          16, 17,  18,  19,  60,  61,  50,  38,  24,  25,  63,  39,  28,  29,  30,  31,
2585          64, 79,  127, 123, 91,  108, 80,  125, 77,  93,  92,  78,  107, 96,  75,  97,
2586         240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 122, 94,  76,  126, 110, 111,
2587         124, 193, 194, 195, 196, 197, 198, 199, 200, 201, 209, 210, 211, 212, 213, 214,
2588         215, 216, 217, 226, 227, 228, 229, 230, 231, 232, 233, 74,  224, 90,  95,  109,
2589         121, 129, 130, 131, 132, 133, 134, 135, 136, 137, 145, 146, 147, 148, 149, 150,
2590         151, 152, 153, 162, 163, 164, 165, 166, 167, 168, 169, 192, 106, 208, 161, 7,
2591          32, 33,  34,  35,  36,  21,  6,   23,  40,  41,  42,  43,  44,  9,   10,  27,
2592          48, 49,  26,  51,  52,  53,  54,  8,   56,  57,  58,  59,  4,   20,  62,  225,
2593          65, 66,  67,  68,  69,  70,  71,  72,  73,  81,  82,  83,  84,  85,  86,  87,
2594          88, 89,  98,  99,  100, 101, 102, 103, 104, 105, 112, 113, 114, 115, 116, 117,
2595         118, 119, 120, 128, 138, 139, 140, 141, 142, 143, 144, 154, 155, 156, 157, 158,
2596         159, 160, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183,
2597         184, 185, 186, 187, 188, 189, 190, 191, 202, 203, 204, 205, 206, 207, 218, 219,
2598         220, 221, 222, 223, 234, 235, 236, 237, 238, 239, 250, 251, 252, 253, 254, 255
2599 };
2600 
2601 const unsigned char e2a[256] = {
2602           0, 1,   2,   3,   156, 9,   134, 127, 151, 141, 142, 11,  12,  13,  14,  15,
2603          16, 17,  18,  19,  157, 133, 8,   135, 24,  25,  146, 143, 28,  29,  30,  31,
2604         128, 129, 130, 131, 132, 10,  23,  27,  136, 137, 138, 139, 140, 5,   6,   7,
2605         144, 145, 22,  147, 148, 149, 150, 4,   152, 153, 154, 155, 20,  21,  158, 26,
2606          32, 160, 161, 162, 163, 164, 165, 166, 167, 168, 91,  46,  60,  40,  43,  33,
2607          38, 169, 170, 171, 172, 173, 174, 175, 176, 177, 93,  36,  42,  41,  59,  94,
2608          45, 47,  178, 179, 180, 181, 182, 183, 184, 185, 124, 44,  37,  95,  62,  63,
2609         186, 187, 188, 189, 190, 191, 192, 193, 194, 96,  58,  35,  64,  39,  61,  34,
2610         195, 97,  98,  99,  100, 101, 102, 103, 104, 105, 196, 197, 198, 199, 200, 201,
2611         202, 106, 107, 108, 109, 110, 111, 112, 113, 114, 203, 204, 205, 206, 207, 208,
2612         209, 126, 115, 116, 117, 118, 119, 120, 121, 122, 210, 211, 212, 213, 214, 215,
2613         216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231,
2614         123, 65,  66,  67,  68,  69,  70,  71,  72,  73,  232, 233, 234, 235, 236, 237,
2615         125, 74,  75,  76,  77,  78,  79,  80,  81,  82,  238, 239, 240, 241, 242, 243,
2616          92, 159, 83,  84,  85,  86,  87,  88,  89,  90,  244, 245, 246, 247, 248, 249,
2617          48, 49,  50,  51,  52,  53,  54,  55,  56,  57,  250, 251, 252, 253, 254, 255
2618 };
2619 
2620 
2621 
2622 
2623 
2624 
2625 
2626 
2627 
2628 
2629 
2630 
2631 
2632 static void fnp3270Msg (uv_tcp_t * client, unsigned char * msg)
     /* [previous][next][first][last][top][bottom][index][help] */
2633   {
2634 //sim_printf ("%s", msg);
2635     size_t l = strlen ((char *) msg);
2636     unsigned char buf [l + 1];
2637     for (uint i = 0; i < l; i ++)
2638       buf[i] = a2e[msg[i]];
2639     buf[l] = '\0';
2640 // command  Erase write 245  (xf5)
2641 // WCC      66 x42 0100 0010   Reset, KB restore
2642 //  SBA     17 x11
2643 // 1st addr byte 64
2644 // 2nd addr byte 64
2645 // start field 29 x1D
2646 // arg  96
2647 //          29, 200, 133, 153, 131, 164, 147, 133 ???
2648     //unsigned char EW [] = {245, 66, 17, 64, 64 };
2649     unsigned char EW [] = {245, 0xc3, 17, 64, 64 };
2650     fnpuv_start_3270_write (client, EW, sizeof (EW));
2651     fnpuv_start_3270_write (client, buf, (ssize_t) l);
2652     fnpuv_send_eor (client);
2653   }
2654 
2655 void fnp3270ConnectPrompt (uv_tcp_t * client)
     /* [previous][next][first][last][top][bottom][index][help] */
2656   {
2657     if (! client || ! client->data)
2658       {
2659         sim_warn ("fnp3270ConnectPrompt bad client data\r\n");
2660         return;
2661       }
2662     uint fnpno       = fnpData.ibm3270ctlr[ASSUME0].fnpno;
2663     uint lineno      = fnpData.ibm3270ctlr[ASSUME0].lineno;
2664     //struct t_line * linep = & fnpData.fnpUnitData[fnpno].MState.line[lineno];
2665     uvClientData * p = client->data;
2666     p->assoc         = true;
2667     p->fnpno         = fnpno;
2668     p->lineno        = lineno;
2669     //fnpData.fnpUnitData[fnpno].MState.line[lineno].line_client = client;
2670 
2671 
2672     // Don't know ttype yet because Telnet negotiation won't
2673     // start until evPoll runs.
2674     unsigned char buf [256];
2675     (void)sprintf ((char *) buf, "DPS8/M 3270 connection to %c.%03d.%ld ttype %s\r\n",
2676                    fnpno+'a',lineno, (long)p->stationNo, p->ttype);
2677     fnpData.ibm3270ctlr[ASSUME0].selDevChar = addr_map[p->stationNo];
2678     fnp3270Msg (client, buf);
2679 
2680   }
2681 
2682 void processLineInput (uv_tcp_t * client, unsigned char * buf, ssize_t nread)
     /* [previous][next][first][last][top][bottom][index][help] */
2683   {
2684     if (! client || ! client->data)
2685       {
2686         sim_warn ("processLineInput bad client data\r\n");
2687         return;
2688       }
2689     uvClientData * p = (uvClientData *) client->data;
2690     uint fnpno       = p -> fnpno;
2691     uint lineno      = p -> lineno;
2692     if (fnpno >= N_FNP_UNITS_MAX || lineno >= MAX_LINES)
2693       {
2694         sim_printf ("bogus client data\r\n");
2695         return;
2696       }
2697 //sim_printf ("assoc. %ld.%ld nread %ld\r\n", (long) fnpno, (long) lineno, (long) nread);
2698 //{for (int i = 0; i < nread; i ++) sim_printf ("%c", isgraph (e2a[buf[i]]) ? e2a[buf[i]] : '.');
2699 //sim_printf ("\r\n");
2700 //for (int i = 0; i < nread; i ++) sim_printf (" %02x", buf[i]);
2701 //sim_printf ("\r\n");
2702 //}
2703 
2704     struct t_line * linep = & fnpData.fnpUnitData[fnpno].MState.line[lineno];
2705 
2706 // By design, inBuffer overrun shouldn't happen, but it has been seen in IMFT.
2707 // (When the TCP backs up, the buffers are merged so that larger and larger
2708 // reads occur. When the backed-up buffer exceeds 65536, libuv calls the read
2709 // callback twice in a row, once with the first 65536, and the next with the
2710 // remaining.
2711 // Cope with it my realloc'ing the buffer and appending the new data. Ugh.
2712     if (linep->inBuffer)
2713       {
2714         if (! sim_quiet)
2715           {
2716             sim_warn ("inBuffer overrun\r\n");
2717           }
2718         unsigned char * new = realloc (linep->inBuffer, (unsigned long) (linep->inSize + nread));
2719         if (! new)
2720           {
2721             sim_warn ("inBuffer realloc fail; dropping data\r\n");
2722             goto done;
2723           }
2724         memcpy (new + linep->inSize, buf, (unsigned long) nread);
2725         linep->inSize   += nread;
2726         linep->inBuffer  = new;
2727       }
2728     else
2729       {
2730         linep->inBuffer = malloc ((unsigned long) nread);
2731         if (! linep->inBuffer)
2732           {
2733             sim_warn ("inBuffer malloc fail;  dropping data\r\n");
2734             goto done;
2735           }
2736         memcpy (linep->inBuffer, buf, (unsigned long) nread);
2737         linep->inSize = (uint) nread;
2738         linep->inUsed = 0;
2739       }
2740 
2741 done:;
2742     // Prevent further reading until this buffer is consumed
2743     fnpuv_read_stop (client);
2744   }
2745 
2746 void process3270Input (uv_tcp_t * client, unsigned char * buf, ssize_t nread)
     /* [previous][next][first][last][top][bottom][index][help] */
2747   {
2748 #if defined(TESTING)
2749     cpu_state_t * cpup = _cpup;
2750 #endif
2751     if (! client || ! client->data)
2752       {
2753         sim_warn ("process3270Input bad client data\r\n");
2754         return;
2755       }
2756     uvClientData * p = (uvClientData *) client->data;
2757     uint fnpno       = p->fnpno;
2758     uint lineno      = p->lineno;
2759     uint stn_no      = p->stationNo;
2760 
2761     if (fnpno >= N_FNP_UNITS_MAX || lineno >= MAX_LINES)
2762       {
2763         sim_printf ("bogus client data\r\n");
2764         return;
2765       }
2766     if_sim_debug (DBG_TRACE, & fnp_dev) {
2767         sim_debug (DBG_TRACE, & fnp_dev, "process3270Input nread %ld\r\n", (long)nread);
2768         for (int i = 0; i < nread; i ++) sim_debug (DBG_TRACE, & fnp_dev, "%c", isgraph (e2a[buf[i]]) ? e2a[buf[i]] : '.');
2769         sim_debug (DBG_TRACE, & fnp_dev, "\r\n");
2770         for (int i = 0; i < nread; i ++) sim_debug (DBG_TRACE, & fnp_dev, " %02x", buf[i]);
2771         sim_debug (DBG_TRACE, & fnp_dev, "\r\n");
2772     }
2773 
2774     struct t_line * linep = & fnpData.fnpUnitData[fnpno].MState.line[lineno];
2775     if (! fnpData.fnpUnitData[fnpno].MState.accept_calls)
2776       {
2777         if (! linep->inBuffer)
2778           fnp3270Msg (client, (unsigned char *) "Multics is not accepting calls\r\n");
2779         return;
2780       }
2781     if (! linep->listen)
2782       {
2783         if (! linep->inBuffer)
2784           fnp3270Msg (client, (unsigned char *) "Multics is not listening to this line\r\n");
2785         return;
2786       }
2787 
2788 // By design, inBuffer overrun shouldn't happen, but it has been seen in IMFT.
2789 // (When the TCP backs up, the buffers are merged so that larger and larger
2790 // reads occur. When the backed-up buffer exceeds 65536, libuv calls the read
2791 // callback twice in a row, once with the first 65536, and the next with the
2792 // remaining.
2793 // Cope with it my realloc'ing the buffer and appending the new data. Ugh.
2794 
2795     struct station_s * stn_p = & fnpData.ibm3270ctlr[ASSUME0].stations[stn_no];
2796     if (stn_p->stn_in_buffer)
2797       {
2798         if (! sim_quiet)
2799           {
2800             sim_warn ("stn_in_buffer overrun\r\n");
2801           }
2802         unsigned char * new = realloc (stn_p->stn_in_buffer, (unsigned long) (stn_p->stn_in_size + nread));
2803         if (! new)
2804           {
2805             sim_warn ("stn_in_buffer realloc fail; dropping data\r\n");
2806             goto done;
2807           }
2808         memcpy (new + stn_p->stn_in_size, buf, (unsigned long) nread);
2809         stn_p->stn_in_size   += nread;
2810         stn_p->stn_in_buffer  = new;
2811       }
2812     else
2813       {
2814         stn_p->stn_in_buffer = malloc ((unsigned long) nread);
2815         if (! stn_p->stn_in_buffer)
2816           {
2817             sim_warn ("stn_in_buffer malloc fail;  dropping data\r\n");
2818             goto done;
2819           }
2820         memcpy (stn_p->stn_in_buffer, buf, (unsigned long) nread);
2821         stn_p->stn_in_size = (uint) nread;
2822         stn_p->stn_in_used = 0;
2823       }
2824 
2825 sim_debug (DBG_TRACE, & fnp_dev,
2826            "process3270Input stashed %lu bytes in stn %u; stn_in_size now %u\r\n",
2827            (unsigned long)nread, stn_no, stn_p->stn_in_size);
2828 done:;
2829     // Prevent further reading until this buffer is consumed
2830     // Rely on 3270 keyboard logic protocol to prevent buffer collision
2831     //fnpuv_read_stop (client);
2832   }
2833 
2834 void reset_line (struct t_line * linep)
     /* [previous][next][first][last][top][bottom][index][help] */
2835   {
2836     linep->was_CR                  = false;
2837     linep->inputBufferSize         = 0;
2838     linep->ctrlStrIdx              = 0;
2839     linep->breakAll                = false;
2840     linep->handleQuit              = false;
2841     linep->echoPlex                = false;
2842     linep->crecho                  = false;
2843     linep->lfecho                  = false;
2844     linep->tabecho                 = false;
2845     linep->replay                  = false;
2846     linep->polite                  = false;
2847     linep->prefixnl                = false;
2848     linep->eight_bit_out           = false;
2849     linep->eight_bit_in            = false;
2850     linep->odd_parity              = false;
2851     linep->output_flow_control     = false;
2852     linep->input_flow_control      = false;
2853     linep->block_xfer_in_frame_sz  = 0;
2854     linep->block_xfer_out_frame_sz = 0;
2855     (void)memset (linep->delay_table,      0, sizeof (linep->delay_table));
2856     linep->inputSuspendLen         = 0;
2857     (void)memset (linep->inputSuspendStr,  0, sizeof (linep->inputSuspendStr));
2858     linep->inputResumeLen          = 0;
2859     (void)memset (linep->inputResumeStr,   0, sizeof (linep->inputResumeStr));
2860     linep->outputSuspendLen        = 0;
2861     (void)memset (linep->outputSuspendStr, 0, sizeof (linep->outputSuspendStr));
2862     linep->outputResumeLen         = 0;
2863     (void)memset (linep->outputResumeStr,  0, sizeof (linep->outputResumeStr));
2864     linep->frame_begin             = 0;
2865     linep->frame_end               = 0;
2866     (void)memset (linep->echnego_break_table, 0, sizeof (linep->echnego_break_table));
2867     linep->echnego_sync_ctr        = 0;
2868     linep->echnego_screen_left     = 0;
2869     linep->echnego_unechoed_cnt    = 0;
2870     linep->echnego_on              = false;
2871     linep->echnego_synced          = false;
2872     linep->line_break              = false;
2873   }
2874 
2875 void processUserInput (uv_tcp_t * client, unsigned char * buf, ssize_t nread)
     /* [previous][next][first][last][top][bottom][index][help] */
2876   {
2877     if (! client || ! client->data)
2878       {
2879         sim_warn ("processUserInput bad client data\r\n");
2880         return;
2881       }
2882     uvClientData * p = (uvClientData *) client->data;
2883     for (ssize_t nchar = 0; nchar < nread; nchar ++)
2884       {
2885         unsigned char kar = buf [nchar];
2886 
2887         if (kar == 0x1b || kar == 0x03)             // ESCape ('\e') | ^C
2888           {
2889             close_connection ((uv_stream_t *) client);
2890             return;
2891           }
2892 
2893         // buffer too full for anything more?
2894         if (p->nPos >= sizeof(p->buffer) - 1)
2895           {
2896             // yes. Only allow \n, \r, ^H, ^R
2897             switch (kar)
2898               {
2899                 case '\b':  // backspace
2900                 case 127:   // delete
2901                   {
2902                     if (p->nPos) //-V547
2903                       {
2904                         fnpuv_start_writestr (client, (unsigned char *) "\b \b");
2905                                                      // removes char from line
2906                         p->buffer[p->nPos]  = 0;     // remove char from buffer
2907                         p->nPos            -= 1;     // back up buffer pointer
2908                       }
2909                   }
2910                   break;
2911 
2912                 case '\n':
2913                 case '\r':
2914                   {
2915                     p->buffer[p->nPos] = 0;
2916                     goto check;
2917                   }
2918 
2919                 case 0x12:  // ^R
2920                   {
2921                     fnpuv_start_writestr (client, (unsigned char *) "^R\r\n");       // echo ^R
2922                     fnpConnectPrompt (client);
2923                     fnpuv_start_writestr (client, (unsigned char *) p->buffer);
2924                   }
2925                  break;
2926 
2927                 default:
2928                   break;
2929               } // switch kar
2930             continue; // process next character in buffer
2931           } // if buffer full
2932 
2933         if (isprint (kar))   // printable?
2934           {
2935             unsigned char str [2] = { kar, 0 };
2936             fnpuv_start_writestr (client,  str);
2937             p->buffer[p->nPos++]  = (char) kar;
2938           }
2939         else
2940           {
2941             switch (kar)
2942               {
2943                 case '\b':  // backspace
2944                 case 127:   // delete
2945                   {
2946                     if (p->nPos)
2947                       {
2948                         fnpuv_start_writestr (client, (unsigned char *) "\b \b");
2949                                                      // removed char from line
2950                         p->buffer[p->nPos]  = 0;     // remove char from buffer
2951                         p->nPos            -= 1;     // back up buffer pointer
2952                       }
2953                   }
2954                   break;
2955 
2956                 case '\n':
2957                 case '\r':
2958                   {
2959                     p->buffer[p->nPos] = 0;
2960                     goto check;
2961                   }
2962 
2963                 case 0x12:  // ^R
2964                   {
2965                     fnpuv_start_writestr (client, (unsigned char *) "^R\r\n");       // echo ^R
2966                     fnpConnectPrompt (client);
2967                     fnpuv_start_writestr (client, (unsigned char *) p->buffer);
2968                   }
2969                   break;
2970 
2971                 default:
2972                   break;
2973               } // switch kar
2974           } // not printable
2975       } // for nchar
2976     return;
2977 
2978 check:;
2979     char cpy [p->nPos + 1];
2980     memcpy (cpy, p->buffer, p->nPos);
2981     cpy [p->nPos] = 0;
2982     trim (cpy);
2983 #if defined(TESTING)
2984     if (! sim_quiet)
2985       {
2986         sim_printf ("[FNP emulation: Rcvd: '%s']\r\n", cpy);
2987       }
2988 #endif
2989     p->nPos = 0;
2990     fnpuv_start_writestr (client, (unsigned char *) "\r\n");
2991 
2992     uint fnp_unit_idx = 0;
2993     uint lineno       = 0;
2994 
2995     if (strlen (cpy))
2996       {
2997         char fnpcode;
2998         int cnt = sscanf (cpy, "%c.h%u", & fnpcode, & lineno);
2999 //sim_printf ("cnt %d fnpcode %c lineno %d\r\n", cnt, fnpcode, lineno);
3000         if (cnt != 2 || fnpcode < 'a' || fnpcode > 'h' || lineno >= MAX_LINES)
3001           {
3002             fnpuv_start_writestr (client, (unsigned char *) "can't parse\r\n");
3003             goto reprompt;
3004           }
3005         fnp_unit_idx = (uint) (fnpcode - 'a');
3006         if (fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno].service != service_login ||
3007             fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno].line_client)
3008           {
3009             fnpuv_start_writestr (client, (unsigned char *) "not available\r\n");
3010             goto reprompt;
3011           }
3012         goto associate;
3013       }
3014     else
3015       {
3016         uint32 numunits = fnp_dev.numunits;
3017         for (fnp_unit_idx = 0; fnp_unit_idx < numunits; fnp_unit_idx ++)
3018           {
3019             if (! fnpData.fnpUnitData[fnp_unit_idx].fnpIsRunning)
3020               continue;
3021             for (lineno = 0; lineno < MAX_LINES; lineno ++)
3022               {
3023                 if (fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno].service == service_login &&
3024                     fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno].listen &&
3025                     ! fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno].line_disconnected &&
3026                     ! fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno].line_client &&
3027                     fnpData.fnpUnitData[fnp_unit_idx].MState.accept_calls)
3028                   {
3029                     goto associate;
3030                   }
3031               }
3032           }
3033         fnpuv_start_writestr (client, (unsigned char *) "not available\r\n");
3034         goto reprompt;
3035       }
3036 reprompt:;
3037     fnpConnectPrompt (client);
3038     return;
3039 
3040 associate:;
3041 
3042     fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno].line_client = client;
3043 //sim_printf ("associated %c.%03d %p\r\n", fnp_unit_idx + 'a', lineno, client);
3044     p->assoc           = true;
3045     p->fnpno           = fnp_unit_idx;
3046     p->lineno          = lineno;
3047     p->read_cb         = fnpuv_associated_readcb;
3048     p->write_cb        = fnpuv_start_write;
3049     p->write_actual_cb = fnpuv_start_write_actual;
3050     // Only enable read when Multics can accept it.
3051     //uv_read_stop ((uv_stream_t *) client);
3052 
3053     char buf2 [1024];
3054 
3055     struct sockaddr name;
3056     int namelen = sizeof (name);
3057     int ret     = uv_tcp_getpeername (client, & name, & namelen);
3058     if (ret < 0)
3059       {
3060         sim_printf ("\r[FNP emulation: CONNECT (addr err %d) to %c.h%03d]\r\n", ret, fnp_unit_idx +'a', lineno);
3061       }
3062     else
3063       {
3064         struct sockaddr_in * p = (struct sockaddr_in *) & name;
3065         sim_printf ("\r[FNP emulation: CONNECT %s to %c.h%03d]\r\n", inet_ntoa (p -> sin_addr), fnp_unit_idx +'a', lineno);
3066       }
3067 
3068     (void)sprintf (buf2, "Attached to line %c.h%03d\r\n", fnp_unit_idx +'a', lineno);
3069     fnpuv_start_writestr (client, (unsigned char *) buf2);
3070 
3071     if (! fnpData.fnpUnitData[fnp_unit_idx].MState.accept_calls)
3072       fnpuv_start_writestr (client, (unsigned char *) "Multics is not accepting calls\r\n");
3073     else if (! fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno].listen)
3074       fnpuv_start_writestr (client, (unsigned char *) "Multics is not listening to this line\r\n");
3075 
3076     // Set from CMF data now.
3077     //fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno].lineType = 1 /* LINE_ASCII */;
3078     if (fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno].lineType == 0) /* LINE_NONE */
3079       fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno].lineType = 1; /* LINE_ASCII */
3080     fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno].accept_new_terminal = true;
3081     reset_line (& fnpData.fnpUnitData[fnp_unit_idx].MState.line[lineno]);
3082   }
3083 
3084 void startFNPListener (void)
     /* [previous][next][first][last][top][bottom][index][help] */
3085   {
3086     (void)fnpuvInit (fnpData.telnet_port, fnpData.telnet_address);
3087   }

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