1 /****^  *********************************************************
   2         *                                                       *
   3         * Copyright, (C) BULL HN Information Systems Inc., 1989 *
   4         *                                                       *
   5         * Copyright, (C) Honeywell Bull Inc., 1988              *
   6         *                                                       *
   7         * Copyright (c) 1972 by Massachusetts Institute of      *
   8         * Technology and Honeywell Information Systems, Inc.    *
   9         *                                                       *
  10         ********************************************************* */
  11 
  12 
  13 
  14 /****^  HISTORY COMMENTS:
  15   1) change(88-06-09,Berno), approve(88-07-13,MCR7928),
  16      audit(88-06-09,Parisek), install(88-07-19,MR12.2-1061):
  17      Initially extracted from dn355.pl1, this module implements the
  18      Multics DSA UNCP multiplexer interface.
  19   2) change(89-04-03,Farley), approve(89-04-24,MECR0010),
  20      audit(89-04-04,Parisek), install(89-04-24,MR12.3-1031):
  21      Modified sub-mailbox management to allow holding of information in local
  22      storage after sub-mbx is freed during interrupt processing and to
  23      centeralize the assignment of the sub-mbxes.  Changed return_mbx/send_mbx
  24      subroutine to re-assign sub-mbx and copy local storage into sub-mbx when
  25      required.
  26   3) change(89-06-01,Farley), approve(89-06-01,MCR8109),
  27      audit(89-06-02,Parisek), install(89-06-01,MR12.3-1052):
  28      Offical installation of above changes..
  29                                                    END HISTORY COMMENTS */
  30 
  31 /* format: style4,insnl,delnl,^ifthendo */
  32 uncp:
  33      procedure;
  34           return;                                           /* should never be called here */
  35 
  36 /*        Date last modified and reason
  37 
  38    Written 10/08/74 by F. A. Canali for new tty dim
  39    Modified by Robert Coren and Mike Grady to fix bugs and add features
  40    Modified by Robert Coren 10/08/75 for multiple 355s
  41    Modified by J. Stern 04/22/77 to introduce WTCBs
  42    Modified by J. Stern 06/23/77 to behave correctly when no submailboxes available
  43    Modified by J. Stern 07/28/77 to use all 3 words of command data in submailbox
  44    Modified Jan.-Feb. 1978 to use variable-size output buffers and fix some bugs
  45    Modified 3/13/78 by Robert Coren to use clock builtin instead of clock_ and to get correct
  46    time at hangup_fnp_lines entry
  47    Modified August 1978 by Robert Coren for demultiplexing
  48    Modified November 8, 1978 by Robert Coren to introduce FNP-initiated mailboxes
  49    Modified July 2 1979 by B. Greenberg for negotiated echo.
  50    Modified March 1980 by Robert Coren to eliminate use of circular buffer.
  51    Modified April 1980 by Robert Coren to add metering information.
  52    Modified 1980 December by Art Beattie to ignore interrupts in invalid levels.  Also allowed command_data for
  53    send_wcd operations to be 216 bits long (6 * 36-bit word).  Corrected error message documentation.
  54    Modified December 1980 by Robert Coren to handle report_meters opcode
  55    Modified April 1981 by Chris Jones for io_manager conversion
  56 
  57 
  58    THE FOLLOWING HISTORY COMMENTS APPLY TO THE FRENCH VERSION OF dn355.pl1
  59    (uncp.pl1).
  60 
  61    Adapted in January 1982 after version MR8.0 for the Datanet 7100
  62    Installed at level MR9.1 in July 1982.
  63    Correction of out_of_bounds after an ineffectgive read (J. Barre)
  64    Open a connection with 2 terminals to fully test
  65    Installed at level MR10.1 in June 1983
  66    Installed at level MR11.0 in August 1983
  67    * Padding (3) assigned fields missing in dn355_data.incl.pl1.
  68    Installed at level MR12.0 in January 1987
  69 
  70    Date of the last modification: 06/01/1987
  71 */
  72 
  73 /*        TRACE TO DEBUG                */
  74 
  75 dcl  1 trace int static options (constant),
  76        2 load bit (1) unal init ("1"b),
  77        2 mailbox bit (1) unal init ("0"b),
  78        2 buffer_in bit (1) unal init ("0"b),
  79        2 buffer_out bit (1) unal init ("0"b);
  80 
  81 dcl  max_special_name fixed bin int static options (constant) init (12);
  82 dcl  special_name (12) bit (36) int static options (constant) init ("444665252362"b3,
  83                                                             /* "MOVECS" bcd */
  84           "444665250000"b3,                                 /* "MOVE00" bcd */
  85           "444665250101"b3,                                 /* "MOVE11" bcd */
  86           "534521623145"b3,                                 /* "$NASIN" bcd */
  87           "534521624646"b3,                                 /* "$NASOO" bcd */
  88           "446443633145"b3,                                 /* "MULTIN" bcd */
  89           "446443634646"b3,                                 /* "MULTOO" bcd */
  90           "534346272020"b3,                                 /* "$LOG  " bcd */
  91           "434621242020"b3,                                 /* "LOAD  " bcd */
  92           "512226202020"b3,                                 /* "RBF   " bcd */
  93           "512226203145"b3,                                 /* "RBF IN" bcd */
  94           "512226204646"b3);                                /* "RBF OO" bcd */
  95 
  96 dcl  dial_name (12) char (8) int static options (constant) init ("d FTF
  97 ", "d FTF
  98 ", "d FTF
  99 ", "d NASF
 100 ", "d NASF
 101 ", "Multics", "Multics", "d LOG
 102 ", "d LOAD
 103 ", "d RBF
 104 ", "rbfdsa", "rbfdsa");
 105 
 106 dcl  qorig fixed bin (24),
 107      da fixed bin (24);
 108 
 109 dcl  sub_mbx_sent bit (1);                                  /* flag to indicate actual sub_mbx usage */
 110 dcl  sub_mbx_no fixed bin;                                  /* sub mailbox index */
 111 dcl  sub_mbx_array (8) bit (36) aligned based (subp);       /* bit overlay on mbx's */
 112 dcl  1 local_sub_mbx aligned like sub_mbx;                  /* local copy of sub_mbx */
 113 
 114 dcl  (bufp, charp, qptr, other_pcbp) ptr,                   /* random pointers used */
 115      timw fixed bin (24),                                   /* local slot for mailbox timw */
 116      (level, dno, i, ix, q_first, q_count, chars_left, numchars, k, j) fixed bin,
 117                                                             /* random halfwords used */
 118      devx fixed bin,                                        /* index of current channel */
 119      operation fixed bin (8),                               /* local slot for delay queue operation */
 120      lano bit (3) unal,                                     /* local slot for line number */
 121      nblocks fixed bin;                                     /* number of blocks allocated */
 122 
 123 dcl  fnp_name char (1) aligned;                             /* for syserr calls */
 124 
 125 dcl  no_response bit (1) aligned;                           /* set by send_mbx to indicate that DIA didn't respond */
 126 
 127 dcl  interrupt_entry bit (1);                               /* whether entered through dn7100$interrupt */
 128 dcl  input_count fixed bin;                                 /* count sent with accept_dir_input */
 129 dcl  inchain bit (18);                                      /* pointer to head of newly-allocated input chain */
 130 
 131 dcl  chan_lctep ptr;                                        /* pointer to subchannel's LCTE */
 132 dcl  inchain_ptr ptr;                                       /* pointer to new input chain */
 133 dcl  bits_per_char fixed bin;
 134 dcl  max_buf_chars fixed bin;                               /* number of characters to go in largest buffer at this speed */
 135 
 136 dcl  wire_arg fixed bin (71);
 137 dcl  wire_ptr ptr;
 138 dcl  masked bit (1);
 139 dcl  queue_locked bit (1);
 140 
 141 dcl  syserr_severity fixed bin (35);
 142 
 143 dcl  SYSERR_announce fixed bin int static options (constant) init (0);
 144 dcl  SYSERR_crash fixed bin int static options (constant) init (1);
 145 dcl  SYSERR_beep fixed bin int static options (constant) init (3);
 146 dcl  SYSERR_log fixed bin int static options (constant) init (4);
 147                                                             /* These syserr constants are used in place of the
 148                                                                constants defined in syserr_constants.incl.pl1
 149                                                                because of the use of the "CRASH" constant
 150                                                                declared in mcs_interrupt_info.incl.pl1 which
 151                                                                is also declared as a constant in syserr_constants.incl.pl1. */
 152 
 153 dcl  LOOP_LIMIT fixed bin int static options (constant) init (100000);
 154                                                             /* used to wait for DIA to clear PCW */
 155 dcl  FNP_DOWN fixed bin int static options (constant) init (2);
 156 
 157 dcl  initial_pcw bit (36) int static init ("000000000000000000000000000000111001"b);
 158                                                             /* initial dia pcw */
 159 
 160 dcl  INT_LEVEL_1 fixed bin int static options (constant) init (1);
 161 dcl  INT_LEVEL_3 fixed bin int static options (constant) init (3);
 162 dcl  INT_LEVEL_7 fixed bin int static options (constant) init (7);
 163 
 164 dcl  BAUD_9600 fixed bin int static options (constant) init (9600);
 165 
 166 dcl  MAX_FREE_BUFFERS fixed bin (35) int static options (constant) init (16000000000);
 167 
 168 dcl  BPC_10 fixed bin int static options (constant) init (10);
 169 dcl  BPC_8 fixed bin int static options (constant) init (8);/* bits per character */
 170 
 171 dcl  MAX_CHANNEL_BUFFER fixed bin int static options (constant) init (128);
 172 
 173 dcl  CMD_DATA_LEN_3 fixed bin int static options (constant) init (3);
 174                                                             /* length of command_data in 6 bit chars */
 175 
 176 dcl  MAX_MBX_REQ_CNT fixed bin (35) int static options (constant) init (262143);
 177 
 178 dcl  EIGHT fixed bin int static options (constant) init (8);
 179 
 180 dcl  CONNECTION_TYPE_1 bit (12) int static options (constant) init ("3145"b3);
 181 dcl  CONNECTION_TYPE_2 bit (12) int static options (constant) init ("4646"b3);
 182 
 183 dcl  timwb (0:6) bit (1) based (addr (timw)),               /* timw as a bit array */
 184      used_string bit (7) based (addr (datanet_mbx.mbx_used_flags.used (0)));
 185                                                             /* mailbox used flags as a bit string */
 186                                                             /* The 8th sub-mbx is not used */
 187 
 188 dcl  (addr, substr, stac, stacq, string, ptr, rel, index, fixed, divide, bin, max, min, null, length, bit, unspec, hbound,
 189      lbound, size, verify) builtin;                         /* builtin functions used */
 190 
 191 dcl  unal_number fixed bin (17) unal based,                 /* handy way of referencing an unaligned number */
 192      chars char (numchars) based;                           /* handy way of moving character strings */
 193 
 194 dcl  input_chars char (chars_left) based;                   /* for scanning entire input */
 195 
 196 dcl  smbx_cmd_data_long bit (216) unaligned based (addr (sub_mbx.command_data));
 197 
 198 dcl  tc_data$system_shutdown ext fixed bin;                 /* external variables used */
 199 dcl  pds$processid bit (36) aligned ext static;
 200 
 201 dcl  ff_cr_lf char (3) int static options (constant) init ("^L^M
 202 ");
 203 
 204 dcl  form_feed init ("^L") char (1) int static options (constant);
 205 
 206 
 207 dcl  syserr entry options (variable),                       /* external entries called */
 208      ldac entry (ptr) returns (fixed bin (24)),
 209      dn355_util$compute_parity entry (bit (36)) returns (bit (36)),
 210      (
 211      uncp_boot_interrupt,
 212      uncp_boot_interrupt$system_fault
 213      ) entry (fixed bin),
 214      uncp_boot_interrupt$request_init entry (fixed bin),
 215      pxss$ring_0_wakeup entry (bit (36) aligned, fixed bin (71), fixed bin (71), fixed bin (35)),
 216      pxss$unique_ring_0_wakeup entry (bit (36) aligned, fixed bin (71), fixed bin (71), fixed bin (35)),
 217      pxss$notify entry (fixed bin);
 218 
 219 dcl  pmut$wire_and_mask entry (fixed bin (71), ptr);
 220 dcl  pmut$unwire_unmask entry (fixed bin (71), ptr);
 221 dcl  1 auto_net_event_message aligned like net_event_message;
 222 dcl  1 auto_fnp_msg aligned like fnp_msg;
 223 dcl  fnp_event_message fixed bin (71);
 224 
 225 dcl  1 q_entry aligned like fnp_queue_entry based (qptr);
 226 
 227 dcl  new_qp ptr;                                            /* temporary to newly-allocated block */
 228 dcl  new_qrel fixed bin;
 229 
 230 dcl  1 wrap_q_address aligned based (addr (sub_mbx.command_data (3))),
 231                                                             /* overlay for rtx on wrapped queue */
 232        2 wrap_ptr bit (18) unal,
 233        2 wrap_cnt fixed bin (18) unsigned unal;
 234 
 235 dcl  fault_name char (16);                                  /* 355 fault name */
 236 
 237 
 238 dcl  cleanup condition;
 239 ^L
 240 %include tty_buf;
 241 
 242 %include uncp_buf;
 243 ^L
 244 %include tty_buffer_block;
 245 
 246 %include lct;
 247 %include pcb;
 248 %include dn355_data;
 249 ^L
 250 dcl  1 gateway_buf aligned based (blockp),
 251        2 cnt fixed bin (18) unsigned unaligned,
 252        2 pad bit (18) unaligned;
 253 
 254 dcl  gateway_header bit (36) based (blockp);
 255 
 256 dcl  buf_words (256) fixed bin based;
 257 ^L
 258 %include dn355_mailbox;
 259 
 260 dcl  connect_to_slave_nowait init (76) fixed bin (8) init static options (constant);
 261 ^L
 262 %include mailbox_ops;
 263 ^L
 264 %include mcs_interrupt_info;
 265 
 266 %include dn355_messages;
 267 ^L
 268 %include fnp_queue_entry;
 269 %include channel_manager_dcls;
 270 %include tty_space_man_dcls;
 271 ^L
 272 %include line_types;
 273 %include baud_rates;
 274 
 275 %include net_event_message;
 276 %page;
 277 %include fnp_mpx_msg_;
 278 %include io_manager_dcls;
 279 %page;
 280 interrupt:
 281      entry (x_dno, x_level, x_status);                      /* entry from iom_manager */
 282 
 283 dcl  x_dno fixed bin (35);                                  /* index from assignment time */
 284 dcl  x_level fixed bin (3);                                 /* interrupt level */
 285 dcl  x_status bit (36) aligned;                             /* status after special or fault */
 286 
 287           if tc_data$system_shutdown ^= 0
 288           then return;                                      /* ignore 355's if shut down in progress */
 289           interrupt_entry = "1"b;
 290 
 291           call setup;
 292           level = x_level;                                  /* copy level to local stack */
 293           if datanet_info.trace
 294           then do;
 295                if level ^= INT_LEVEL_3 | ^fnp_info.running
 296                then syserr_severity = SYSERR_announce;
 297                else syserr_severity = SYSERR_log;
 298                call syserr (syserr_severity,
 299                     "uncp: FNP ^a level ^d status ^w^[ running^]^[ bootloading^]^[ t_and_d_in_progress^]", fnp_name,
 300                     level, x_status, fnp_info.running, fnp_info.bootloading, fnp_info.t_and_d_in_progress);
 301           end;
 302           if level ^= INT_LEVEL_3 & level ^= INT_LEVEL_7
 303           then do;                                          /* if not a good interrupt level */
 304                call syserr (SYSERR_beep, "uncp: FNP ^a invalid interrupt level ^o", fnp_name, level);
 305                if fnp_info.bootloading
 306                then if level = INT_LEVEL_1                  /* system fault */
 307                     then call uncp_boot_interrupt$system_fault (dno);
 308                return;                                      /* lets hope its benign */
 309           end;
 310 
 311           if (^fnp_info.t_and_d_in_progress) & (^fnp_info.running) & (^fnp_info.bootloading)
 312           then return;                                      /* spurious interrupt */
 313 
 314           if ^stac (addr (lcte.lock), pds$processid)        /* somebody else has it */
 315           then do;
 316 
 317                do while (^stac (addr (fnp_info.queue_lock), pds$processid));
 318                end;
 319 
 320                if level = INT_LEVEL_7
 321                then fnp_info.level_7_pending = "1"b;
 322                else fnp_info.level_3_pending = "1"b;
 323 
 324                if stac (addr (lcte.lock), pds$processid)    /* in case it got unlocked meanwhile */
 325                then call process_int_queue ("0"b);
 326 
 327                else if ^stacq (fnp_info.queue_lock, "0"b, pds$processid)
 328                then call syserr (SYSERR_crash, "uncp: inconsistent queue lock");
 329           end;
 330 
 331           else do;
 332                call process_int (level);
 333 
 334                do while (^stac (addr (fnp_info.queue_lock), pds$processid));
 335                end;                                         /* check the queue to see if anything came in while we had the lock */
 336 
 337                call process_int_queue ("0"b);
 338           end;
 339 
 340           return;
 341 
 342 global_exit:                                                /* if abort out of an internal proc */
 343           if interrupt_entry
 344           then do;
 345                if stacq (lcte.lock, "0"b, pds$processid)    /* make sure we undo anything we did */
 346                then if lcte.notify_reqd
 347                     then do;
 348                          lcte.notify_reqd = "0"b;
 349                          call pxss$notify (tty_ev);
 350                     end;
 351 
 352                lcte.locked_for_interrupt = "0"b;
 353 
 354           end;
 355           return;
 356 ^L
 357 /* entry to send a command to the FNP */
 358 send_wcd:
 359      entry (a_fnpp, a_pcbp, opa, chrsa, data);
 360 
 361 dcl  a_fnpp ptr,                                            /* parameters */
 362      a_pcbp ptr,
 363      opa fixed bin (8),
 364      data bit (*),                                          /* allow use of 6 words in sub_mbx */
 365      chrsa fixed bin (8);                                   /* numeric */
 366 
 367 dcl  tdata bit (8 * 36);
 368 dcl  data_len fixed bin (8);
 369 
 370           pcbp = a_pcbp;
 371           go to send_join;
 372 
 373 send_global_wcd:
 374      entry (a_fnpp, opa, chrsa, data);
 375 
 376           pcbp = null ();
 377           fnpp = a_fnpp;
 378           if opa = dial
 379           then do;
 380                do ix = 1 to max_special_name while (special_name (ix) ^= substr (data, 1, 36));
 381                end;
 382                if ix <= max_special_name
 383                then fnp_info.active_bit (ix) = substr (data, 37, 1);
 384                return;
 385           end;
 386 
 387 send_join:
 388           interrupt_entry = "0"b;
 389           fnpp = a_fnpp;
 390           ttybp = addr (tty_buf$);                          /* get ptr to tty buf */
 391           infop = addr (dn355_data$);                       /* and dn 355 info */
 392           uncpbp = datanet_info.uncp_bufp;                  /* get ptr to uncp buf */
 393           lctep = fnp_info.lcte_ptr;
 394           operation = opa;                                  /* and copy op to local stack */
 395           if ^fnp_info.running &                            /* can't talk to it if it's not listening */
 396                (^fnp_info.bootloading | operation ^= init_complete)
 397           then return;
 398 
 399           no_response = "0"b;
 400           dno = fnp_info.fnp_number;
 401           mbxp = fnp_info.mbx_pt;                           /* get pointer to mailbox */
 402           data_len = min (length (tdata), chrsa);           /* compute bit length of command data */
 403           if data_len > 0
 404           then tdata = substr (data, 1, data_len);
 405           else tdata = "0"b;
 406 
 407           if pcbp ^= null
 408           then if operation = disconnect_this_line
 409                then if pcb.dialed = "0"b
 410                     then return;
 411 
 412           if operation = accept_direct_output
 413           then if pcb.dumpout
 414                then do;
 415                     call throw_away_output;
 416                     pcb.dumpout = "0"b;
 417                end;
 418 
 419           if operation = enter_receive
 420           then do;
 421                if pcb.connection_type = "10"b
 422                then return;
 423 
 424                ix = pcb.baud_rate;
 425                if ix ^= BAUD_9600
 426                then do;
 427                     pcb.turn = "1"b;
 428                     call send_dial;
 429                     return;
 430                end;
 431 
 432                if pcb.write_last ^= 0
 433                then do;
 434                     blockp = ptr (ttybp, pcb.write_last);
 435                     buffer.turn = "1"b;
 436                     return;
 437                end;
 438                else do;
 439                     pcb.enter_receive_pending = "1"b;
 440                     if pcb.end_frame | ^pcb.send_output
 441                     then return;
 442                     else operation = accept_direct_output;
 443                end;
 444 
 445           end;
 446 
 447           call assign_sub_mbx (sub_mbx_no, subp);           /* find a free sub mbx */
 448           if sub_mbx_no = -1                                /* no submailbox */
 449           then do;
 450                call make_q_entry (operation, data_len, tdata);
 451                fnp_info.mbx_unavailable = fnp_info.mbx_unavailable + 1;
 452                                                             /* form q entry element from data */
 453           end;
 454 
 455           else do;                                          /* we have a sub mbx, ship it off to the 355 */
 456                if pcbp ^= null ()
 457                then do;
 458                     string (sub_mbx.line_number) = string (pcb.line_number);
 459                                                             /* move line number to sub mbx */
 460                     devx = pcb.devx;
 461                end;
 462 
 463                else string (sub_mbx.line_number) = "0"b;    /* unless no pcb (global call) */
 464 
 465                sub_mbx.op_code = operation;                 /* set sub mbx op */
 466                sub_mbx.cmd_data_len = divide (data_len, 6, 17, 0);
 467                                                             /* set data length */
 468                if operation = accept_direct_output          /* if output op */
 469                then do;
 470                     if ^pcb.flags.dialed                    /* output without a dialup? */
 471                     then call throw_away_output;            /* discard it */
 472 
 473                     else call process_send_output (sub_mbx_no, "0"b);
 474                end;
 475 
 476                else do;
 477                     sub_mbx.io_cmd = wcd;                   /* set write control data cmd */
 478                     smbx_cmd_data_long = substr (tdata, 1, data_len);
 479                                                             /* move command data to sub mbx */
 480                     call send_mbx (sub_mbx_no);             /* ship the mbx off to the 355 */
 481                     fnp_info.output_control_transactions = fnp_info.output_control_transactions + 1;
 482                end;
 483 
 484                if ^sub_mbx_sent
 485                then call release_sub_mbx (sub_mbx_no);
 486 
 487                if no_response
 488                then call report_fnp_no_response;
 489           end;
 490 
 491 
 492           return;                                           /* return to caller */
 493 ^L
 494 process_interrupt_queue:
 495      entry (x_dno);
 496 
 497           interrupt_entry = "0"b;
 498           call setup;
 499           on cleanup call check_lock;
 500           masked = "1"b;                                    /* have to mask and wire while holding queue lock */
 501           call pmut$wire_and_mask (wire_arg, wire_ptr);
 502 
 503           do while (^stac (addr (fnp_info.queue_lock), pds$processid));
 504           end;
 505           queue_locked = "1"b;
 506 
 507           call process_int_queue ("1"b);
 508           return;
 509 ^L
 510 setup:
 511      proc;
 512 
 513           ttybp = addr (tty_buf$);                          /* get addr of tty buffer segment */
 514           dno = x_dno;                                      /* copy 355 number to local stack */
 515           infop = addr (dn355_data$);                       /* get address 0f 355 info segment  */
 516           uncpbp = datanet_info.uncp_bufp;                  /*   initialise l adresse du segment uncp_buf   */
 517 
 518           fnpp = addr (datanet_info.per_datanet (dno));
 519           fnp_name = fnp_info.fnp_tag;
 520           mbxp = fnp_info.mbx_pt;                           /* get mailbox pointer */
 521           lctep = fnp_info.lcte_ptr;
 522           n_pcbs = fnp_info.no_of_channels;                 /* set number of channel control blocks to number of channels */
 523 
 524           return;
 525      end setup;
 526 ^L
 527 process_int_queue:
 528      proc (caller_masked);
 529 
 530 /* called with queue locked. Empties the queue, and must unlock it when done */
 531 
 532 dcl  caller_masked bit (1);                                 /* indicates whether caller explicitly called pmut$wire_and_mask */
 533 
 534           do while (dequeue (level));
 535                fnp_info.processed_from_q = fnp_info.processed_from_q + 1;
 536                                                             /* meter */
 537                if ^stacq (fnp_info.queue_lock, "0"b, pds$processid)
 538                then call syserr (SYSERR_crash, "uncp: inconsistent queue lock");
 539 
 540                queue_locked = "0"b;
 541                if caller_masked
 542                then call pmut$unwire_unmask (wire_arg, wire_ptr);
 543                masked = "0"b;
 544 
 545                call process_int (level);
 546 
 547                if caller_masked
 548                then do;                                     /* if we unmasked, we have to mask again */
 549                     masked = "1"b;
 550                     call pmut$wire_and_mask (wire_arg, wire_ptr);
 551                end;
 552 
 553                do while (^stac (addr (fnp_info.queue_lock), pds$processid));
 554                end;
 555                queue_locked = "1"b;
 556           end;
 557 
 558           lcte.locked_for_interrupt = "0"b;
 559           if ^stacq (lcte.lock, "0"b, pds$processid)
 560           then call syserr (SYSERR_crash, "uncp: LCTE lock ^^= processid");
 561 
 562           if ^stacq (fnp_info.queue_lock, "0"b, pds$processid)
 563           then call syserr (SYSERR_crash, "uncp: inconsistent queue lock");
 564 
 565           queue_locked = "0"b;
 566           if caller_masked
 567           then call pmut$unwire_unmask (wire_arg, wire_ptr);
 568           masked = "0"b;
 569 
 570           if lcte.notify_reqd
 571           then do;
 572                lcte.notify_reqd = "0"b;
 573                call pxss$notify (tty_ev);
 574           end;
 575 
 576           return;
 577 
 578 dequeue:
 579           proc (a_level) returns (bit (1));
 580 
 581 dcl  a_level fixed bin;
 582 
 583                if fnp_info.level_3_pending
 584                then do;
 585                     fnp_info.level_3_pending = "0"b;
 586                     a_level = INT_LEVEL_3;
 587                     return ("1"b);
 588                end;
 589 
 590                else if fnp_info.level_7_pending
 591                then do;
 592                     fnp_info.level_7_pending = "0"b;
 593                     a_level = INT_LEVEL_7;
 594                     return ("1"b);
 595                end;
 596 
 597                else return ("0"b);
 598 
 599           end /* dequeue */;
 600      end /* process_int_queue */;
 601 ^L
 602 process_int:
 603      proc (a_level);
 604 
 605 /* internal procedure to process an interrupt, either when it occurs or from the queue */
 606 
 607 dcl  a_level fixed bin;
 608 
 609           level = a_level;
 610 
 611           lcte.locked_for_interrupt = "1"b;
 612           if level = INT_LEVEL_7
 613           then do;                                          /* emergency interrupt */
 614 
 615                if fnp_info.t_and_d_in_progress
 616                then do;
 617                     if fnp_info.t_and_d_lev_7_occurred
 618                     then return;
 619                     fnp_info.t_and_d_lev_7_occurred = "1"b;
 620 t_and_d_join:
 621                     if fnp_info.t_and_d_notify_requested
 622                     then do;
 623                          call pxss$notify (tty_ev);
 624                          fnp_info.t_and_d_notify_requested = "0"b;
 625                     end;
 626                     unspec (auto_net_event_message) = "0"b;
 627                     auto_net_event_message.version = NET_EVENT_MESSAGE_VERSION_1;
 628                     auto_net_event_message.network_type = MCS_NETWORK_TYPE;
 629                     auto_net_event_message.handle = dno;
 630                     auto_net_event_message.type = level;
 631                     unspec (net_event_message_arg) = unspec (auto_net_event_message);
 632                     call pxss$unique_ring_0_wakeup (fnp_info.boot_process_id, fnp_info.boot_ev_chan,
 633                          net_event_message_arg, 0);
 634                     return;
 635                end;
 636 
 637                fault_name = "STOPPING DATANET";
 638                call syserr (SYSERR_beep, "uncp: emergency interrupt from FNP ^a: ^a", fnp_info.fnp_tag, fault_name);
 639                timw = ldac (addr (datanet_mbx.term_inpt_mpx_wd));
 640                if fnp_info.running
 641                then call purge_write_texte;
 642 
 643 
 644                call report_fnp_crash;                       /* report it and hang up lines */
 645                return;                                      /* done with this interrupt */
 646           end;
 647 
 648           if unspec (datanet_mbx.mailbox_requests) = "777777000000"b3
 649           then do;                                          /* DSA requesting init */
 650                datanet_mbx.mailbox_requests = datanet_mbx.mailbox_requests + 1;
 651 
 652                if trace.load
 653                then call syserr (SYSERR_log, "uncp: request_init");
 654 
 655                timw = ldac (addr (datanet_mbx.term_inpt_mpx_wd));
 656                if fnp_info.running
 657                then call purge_write_texte;
 658                call uncp_boot_interrupt$request_init (dno);
 659                fnp_info.uncp_pcbx1, fnp_info.uncp_pcbx2 = 0;
 660                return;
 661           end;
 662 
 663           if fnp_info.bootloading                           /* if this is bootload status */
 664           then do;
 665                timw = ldac (addr (datanet_mbx.term_inpt_mpx_wd));
 666                if timwb (0)
 667                then do;                                     /* 1st mailbox for WCD_init_complete */
 668                     subp = addr (datanet_mbx.dn355_sub_mbxes (0));
 669                     datanet_mbx.mbx_used_flags.used (0) = "0"b;
 670                                                             /* free the mailbox */
 671                     datanet_mbx.num_in_use = datanet_mbx.num_in_use - 1;
 672                     if (sub_mbx.io_cmd = wcd) & (sub_mbx.op_code = init_complete)
 673                     then do;
 674 
 675                          if trace.load
 676                          then call syserr (SYSERR_log, "uncp: WCD init_complete acknowledgement");
 677 
 678                          call uncp_boot_interrupt (dno);    /* let special routine figure it out */
 679                          fnp_info.uncp_pcbx1, fnp_info.uncp_pcbx2 = 0;
 680                     end;
 681                end;
 682                return;                                      /* Don't analyze the mailbox */
 683           end;
 684 
 685 
 686 
 687 
 688           if fnp_info.t_and_d_in_progress
 689           then do;
 690                if fnp_info.t_and_d_lev_3_occurred
 691                then return;
 692                fnp_info.t_and_d_lev_3_occurred = "1"b;
 693                go to t_and_d_join;
 694           end;
 695 
 696           if ^fnp_info.running                              /* if this interrupt is premature, ignore it */
 697           then return;
 698 
 699 
 700           no_response = "0"b;                               /* initially */
 701 
 702 
 703 
 704 
 705           if used_string ^= (7)"1"b
 706           then call spend_submailboxes;                     /* if some free mailboxes */
 707 ^L
 708 /* process any submailboxes which have been returned by the 355 */
 709 
 710           timw = ldac (addr (datanet_mbx.term_inpt_mpx_wd));/* get timw and clear */
 711 
 712 
 713 /* mailbox number 0 & number  15 are reserved to init and abort */
 714 
 715           do i = lbound (timwb, 1) to hbound (timwb, 1);    /* loop over submailbox indicators */
 716 
 717                if timwb (i) & ^no_response
 718                then do;                                     /* if mailbox was returned by 355 then we have something to do */
 719                     sub_mbx_no = i;
 720                     unspec (local_sub_mbx) = unspec (datanet_mbx.dn355_sub_mbxes (sub_mbx_no));
 721                     subp = addr (local_sub_mbx);
 722                     if trace.mailbox
 723                     then call syserr (SYSERR_log, "uncp: mbx received # ^o ^2( ^/ ^4( ^w ^) ^)", sub_mbx_no,
 724                               sub_mbx_array);
 725                     datanet_mbx.mbx_used_flags.used (sub_mbx_no) = "0"b;
 726                                                             /* clear submailbox used flag */
 727                     sub_mbx_no = -1;                        /* indicate use of local copy */
 728 
 729                     datanet_mbx.num_in_use = datanet_mbx.num_in_use - 1;
 730                     fnp_info.cumulative_mbx_in_use = fnp_info.cumulative_mbx_in_use + datanet_mbx.num_in_use;
 731                     fnp_info.mbx_in_use_updated = fnp_info.mbx_in_use_updated + 1;
 732 
 733 
 734                     call get_line_number;
 735 
 736 /* WRITE COMMAND DATA                   */
 737 
 738                     if sub_mbx.io_cmd = wcd
 739                     then do;
 740                          if (devx ^= -1) & (sub_mbx.op_code = disconnect_this_line)
 741                          then do;
 742                               if pcb.dialed
 743                               then do;
 744                                    call throw_away_output;
 745                                    if pcb.connection_type ^= "01"b
 746                                    then call channel_manager$interrupt (devx, HANGUP, ""b);
 747 disconnect_other_line:
 748                                    if (pcb.connection_type ^= "00"b) & (pcb.uncp_pcbx ^= 0)
 749                                    then do;
 750                                         other_pcbp = addr (fnp_info.pcb_array_ptr -> pcb_array (pcb.uncp_pcbx));
 751                                         if other_pcbp -> pcb.dialed
 752                                         then do;
 753                                              string (sub_mbx.line_number) = string (other_pcbp -> pcb.line_number);
 754                                              call return_mbx (sub_mbx_no);
 755                                         end;
 756                                    end;
 757                               end;
 758                               pcb.dialed = "0"b;
 759                               pcb.baud_rate = 0;
 760                          end;
 761 
 762                          if (devx ^= -1) & (sub_mbx.op_code = disconnect_accepted)
 763                          then do;
 764                               sub_mbx.op_code = disconnect_this_line;
 765                               goto disconnect_other_line;
 766                          end;
 767 
 768 
 769                     end;                                    /* just free submbx */
 770 
 771                     else do;
 772 
 773 /*        WRITE TEXTE                             */
 774 
 775                          if sub_mbx.io_cmd = wtx
 776                          then do;                           /* check for write text */
 777                               pcb.output_mbx_pending = "0"b;
 778                               if pcb.connection_type = "10"b
 779                               then pcbp = addr (fnp_info.pcb_array_ptr -> pcb_array (pcb.uncp_pcbx));
 780                               if sub_mbx.command_data (3) ^= "0"b
 781                               then do;
 782                                    da = bin (sub_mbx.data_addr, 18) - tty_buf.absorig;
 783                                                             /* get offset in tty buf   */
 784                                    blockp = ptr (ttybp, da);/* set ptr to buffer */
 785                                    gateway_header = sub_mbx.command_data (3);
 786 
 787 
 788                                    call tty_space_man$free_chain ((pcb.devx), OUTPUT, blockp);
 789                                                             /* and the output chain */
 790 
 791 
 792 
 793                               end;
 794                          end;
 795 
 796 /*        READ CONTROL DATA             */
 797 
 798                          else if sub_mbx.io_cmd = rcd
 799                          then do;                           /* check for control stuff */
 800                               if (sub_mbx.op_code = accept_direct_input) | (sub_mbx.op_code = send_output)
 801                               then do;
 802 
 803 
 804                                    fnp_info.bleft_355 = 0;  /* make it safe */
 805 
 806                                    if fnp_info.free_size > MAX_FREE_BUFFERS
 807                                    then do;
 808                                         fnp_info.free_size = 0;
 809                                         fnp_info.free_count = 0;
 810                                    end;
 811 
 812                                    fnp_info.free_size = fnp_info.free_size + fnp_info.bleft_355;
 813                                    fnp_info.free_count = fnp_info.free_count + 1;
 814                               end;
 815                               if sub_mbx.op_code = accept_direct_input
 816                               then fnp_info.input_data_transactions = fnp_info.input_data_transactions + 1;
 817                               else fnp_info.input_control_transactions = fnp_info.input_control_transactions + 1;
 818 
 819 
 820 
 821 /*        ACCEPT NEW TERMINAL           */
 822 
 823                               if sub_mbx.op_code = accept_new_terminal
 824                               then do;                      /* check for new terminal on line */
 825                                    if devx = -1
 826                                    then do;
 827                                         sub_mbx.io_cmd = wcd;
 828                                         sub_mbx.op_code = terminal_rejected;
 829                                         sub_mbx.cmd_data_len = 0;
 830                                         call return_mbx (sub_mbx_no);
 831                                    end;
 832                                    else do;
 833 
 834 
 835                                         pcb.line_type = LINE_ASCII;
 836                                         pcb.send_lf = "0"b;
 837 
 838 
 839 
 840                                         pcb.baud_rate = BAUD_9600;
 841                                                             /* highest speed for UNCP */
 842 
 843                                         do j = 1 to n_sync_line_types while (sync_line_type (j) ^= pcb.line_type);
 844                                         end;
 845                                         pcb.sync_line = (j <= n_sync_line_types);
 846 
 847                                         if ^pcb.sync_line   /* asynchronous */
 848                                         then bits_per_char = BPC_10;
 849                                         else bits_per_char = BPC_8;
 850                                                             /* assumption for synchronous lines */
 851 
 852                                         max_buf_chars =
 853                                              divide (divide (pcb.baud_rate, bits_per_char, 17, 0), buf_per_second, 17, 0);
 854                                         pcb.max_buf_size = min (16 * divide (max_buf_chars + 67, 64, 17, 0), 128);
 855                                                             /* round up to multiple of 16 words */
 856                                         if pcb.line_type = LINE_COLTS
 857                                         then pcb.max_buf_size = MAX_CHANNEL_BUFFER;
 858                                                             /* COLTS channel always gets big buffers */
 859                                         pcb.dialed, pcb.enter_receive_pending, pcb.turn, pcb.dumpout, pcb.send_output =
 860                                              "0"b;
 861                                         pcb.connection_type = "00"b;
 862 
 863                                         sub_mbx.op_code = terminal_accepted;
 864                                                             /* inform 355 that term is ok */
 865                                         sub_mbx.cmd_data_len = CMD_DATA_LEN_3;
 866                                                             /* we will put write buffer threshold in command data */
 867                                         if ^pcb.high_speed  /* less than 1200 baud */
 868                                         then addr (sub_mbx.command_data) -> unal_number = 2;
 869                                                             /* set low write buffer threshold */
 870                                         else addr (sub_mbx.command_data) -> unal_number = 4;
 871                                                             /* set high write buffer threshold */
 872                                         sub_mbx.io_cmd = wcd;
 873                                         call return_mbx (sub_mbx_no);
 874                                    end;
 875                               end;
 876 
 877 /*        DISCONNECTED LINE             */
 878 
 879                               else if sub_mbx.op_code = disconnected_line
 880                               then do;                      /* see if line just hung up */
 881                                    if pcb.dialed
 882                                    then do;
 883                                         call throw_away_output;
 884                                         if pcb.connection_type ^= "01"b
 885                                         then call channel_manager$interrupt (devx, HANGUP, ""b);
 886                                    end;
 887                                    pcb.dialed = "0"b;
 888                                    sub_mbx.io_cmd = wcd;
 889                                    sub_mbx.op_code = disconnect_accepted;
 890                                    call return_mbx (sub_mbx_no);
 891                               end;
 892 
 893 /*        BREAK CONDITION               */
 894 
 895                               else if sub_mbx.op_code = break_condition
 896                               then do;                      /* check for break */
 897                                    if pcb.dialed
 898                                    then do;
 899                                         if pcb.hndlquit
 900                                         then call throw_away_output;
 901                                         call channel_manager$interrupt (devx, QUIT, ""b);
 902                                         pcb.turn = "1"b;
 903                                    end;
 904                                    sub_mbx.io_cmd = wcd;
 905                                    sub_mbx.op_code = break_acknowledged;
 906                                    sub_mbx.cmd_data_len = 0;
 907                                    call return_mbx (sub_mbx_no);
 908                                    call make_q_entry (accept_direct_output, 0, ""b);
 909                                                             /* BREAK sends the turn */
 910                               end;
 911 
 912 /*        SEND OUTPUT                   */
 913 
 914                               else if sub_mbx.op_code = send_output
 915                               then do;                      /* is this request for output? */
 916                                    if pcb.dialed
 917                                    then do;
 918                                         if pcb.connection_type = "10"b
 919                                         then pcb.turn = "1"b;
 920                                         call process_send_output (sub_mbx_no, "1"b);
 921                                    end;
 922 
 923                               end;
 924 
 925 /*        ACCEPT DIRECT INPUT           */
 926 
 927                               else if sub_mbx.op_code = accept_direct_input
 928                               then do;                      /* check for input from terminal */
 929                                    if pcb.dialed
 930                                    then call process_accept_input;
 931                               end;
 932 
 933 
 934 /*        CONNECT TO SLAVE              */
 935 /*        CONNECT TO SLAVE WITH NO WAIT */
 936                               else if (sub_mbx.op_code = connect_to_slave) | (sub_mbx.op_code = connect_to_slave_nowait)
 937                               then do;
 938                                    pcb.extra_nl, pcb.lfecho = "0"b;
 939                                    do ix = 1 to max_special_name while (special_name (ix) ^= sub_mbx.command_data (1));
 940                                    end;
 941                                    if ix <= max_special_name
 942                                    then do;
 943                                         if (dial_name (ix) = "Multics") | (dial_name (ix) = "rbfdsa")
 944                                         then fnp_info.active_bit (ix) = "1"b;
 945 
 946                                         if ^fnp_info.active_bit (ix)
 947                                         then do;
 948                                              sub_mbx.io_cmd = wcd;
 949                                              sub_mbx.op_code = disconnect_this_line;
 950                                              call return_mbx (sub_mbx_no);
 951                                              goto no_dialup;
 952                                         end;
 953                                    end;
 954 
 955                                    if ix > max_special_name
 956                                    then do;
 957                                         ix = BAUD_9600;
 958                                         pcb.turn, pcb.extra_nl, pcb.lfecho = "1"b;
 959                                    end;
 960                                    else do;
 961                                         pcb.baud_rate = ix;
 962                                         if index (special_name (ix), CONNECTION_TYPE_1) ^= 0
 963                                         then pcb.connection_type = "01"b;
 964                                         if index (special_name (ix), CONNECTION_TYPE_2) ^= 0
 965                                         then pcb.connection_type = "10"b;
 966                                    end;
 967 
 968                                    pcb.dialed = "1"b;
 969 
 970                                    if pcb.connection_type = "01"b
 971                                    then do;
 972                                         pcb.uncp_pcbx = 0;
 973                                         if fnp_info.uncp_pcbx1 = 0
 974                                         then do;
 975                                              fnp_info.uncp_pcbx1 = j;
 976                                              sub_mbx.op_code = enter_receive;
 977                                              call return_mbx (sub_mbx_no);
 978                                         end;
 979                                         else if fnp_info.uncp_pcbx2 = 0
 980                                         then fnp_info.uncp_pcbx2 = j;
 981                                         goto no_dialup;
 982                                    end;
 983 
 984                                    if pcb.connection_type = "10"b
 985                                    then do;
 986                                         if fnp_info.uncp_pcbx1 = 0
 987                                         then do;
 988                                              call syserr (SYSERR_announce,
 989                                                   "uncp: Connection on output only not awaited. (dial '^a')",
 990                                                   dial_name (ix));
 991                                              goto no_dialup;
 992                                         end;
 993                                         else do;
 994                                              other_pcbp =
 995                                                   addr (fnp_info.pcb_array_ptr -> pcb_array (fnp_info.uncp_pcbx1));
 996                                              other_pcbp -> pcb.uncp_pcbx = j;
 997                                              pcb.uncp_pcbx = fnp_info.uncp_pcbx1;
 998                                              pcb.turn = "1"b;
 999                                              if dial_name (ix) = "Multics"
1000                                              then other_pcbp -> pcb.extra_nl = "1"b;
1001 
1002                                              if (dial_name (ix) = "Multics") | (dial_name (ix) = "rbfdsa")
1003                                              then other_pcbp -> pcb.baud_rate, pcb.baud_rate = BAUD_9600;
1004 
1005 
1006                                              fnp_info.uncp_pcbx1 = 0;
1007                                              if fnp_info.uncp_pcbx2 ^= 0
1008                                              then do;
1009                                                   other_pcbp =
1010                                                        addr (fnp_info.pcb_array_ptr -> pcb_array (fnp_info.uncp_pcbx2));
1011                                                   fnp_info.uncp_pcbx1 = fnp_info.uncp_pcbx2;
1012                                                   fnp_info.uncp_pcbx2 = 0;
1013                                                   string (sub_mbx.line_number) = string (other_pcbp -> pcb.line_number);
1014                                                   sub_mbx.op_code = enter_receive;
1015                                                   call return_mbx (sub_mbx_no);
1016                                                   j = fnp_info.hsla_idx (0);
1017                                                   do j = j to fnp_info.no_of_channels while (fnp_info.uncp_pcbx2 = 0);
1018                                                        other_pcbp = addr (fnp_info.pcb_array_ptr -> pcb_array (j));
1019                                                        if other_pcbp -> pcb.dialed
1020                                                             & (other_pcbp -> pcb.connection_type = "01"b)
1021                                                             & (other_pcbp -> pcb.uncp_pcbx = 0)
1022                                                        then if (fnp_info.uncp_pcbx1 ^= j)
1023                                                             then fnp_info.uncp_pcbx2 = j;
1024                                                   end;
1025                                              end;
1026                                         end;
1027                                    end;
1028 
1029                                    dialup_info.line_type = LINE_ASCII;
1030                                    dialup_info.receive_mode_device = "0"b;
1031                                    if pcb.connection_type = "00"b
1032                                    then dialup_info.receive_mode_device = "1"b;
1033                                    dialup_info.baud_rate = ix;
1034                                    dialup_info.max_buf_size = pcb.max_buf_size;
1035                                    dialup_info.buffer_pad = 0;
1036                                    dialup_info.pad = "0"b;
1037                                    interrupt_info = unspec (dialup_info);
1038                                    call channel_manager$interrupt (devx, DIALUP, interrupt_info);
1039                                    call process_send_output (sub_mbx_no, "1"b);
1040 no_dialup:
1041                               end;
1042 
1043                               else if devx ^= -1
1044                               then call syserr (SYSERR_announce,
1045                                         "uncp: unrecognized op code ^o with rcd from FNP ^a for devx ^o", sub_mbx.op_code,
1046                                         substr ("abcdefgh", dno, 1), devx);
1047                                                             /* someone goofed */
1048 
1049                          end;
1050 
1051 /*        READ TEXTE                              */
1052 
1053                          else if sub_mbx.io_cmd = rtx
1054                          then do;
1055                               call process_rtx;             /* check for read text */
1056                               if pcb.connection_type = "01"b
1057                               then do;
1058                                    sub_mbx.op_code = enter_receive;
1059                                    call return_mbx (sub_mbx_no);
1060                               end;
1061                          end;
1062                          else call syserr (SYSERR_beep, "uncp: unrecognized io command ^o from FNP ^a for line ^o",
1063                                    sub_mbx.io_cmd, substr ("abcdefgh", dno, 1), bin (string (sub_mbx.line_number), 10));
1064                                                             /* complain */
1065                     end;
1066 
1067 
1068                end;
1069           end;
1070 
1071 /* use spend_submailboxes    */
1072 
1073 
1074           if ^no_response & used_string ^= (7)"1"b
1075           then                                              /* see if we freed some submailboxes */
1076                call spend_submailboxes;
1077 
1078 
1079 
1080 
1081           if no_response                                    /* if someone discovered that the FNP was gone */
1082           then call report_fnp_no_response;
1083 
1084 /* Supprimer le label exit avec deverrouillage de lcte.lock   */
1085 
1086           return;                                           /* return to iom_manager */
1087      end /* process_int  */;
1088 ^L
1089 /* An internal procedure to perform interrupt-time tasks that consume submailboxes.
1090    First check for queued up work. Then attend to FNP mailbox requests. */
1091 
1092 spend_submailboxes:
1093      proc;
1094 
1095           if fnp_info.count ^= 0
1096           then call process_q;                              /* were we waiting for a free mbx */
1097 
1098 
1099           do while (datanet_mbx.mailbox_requests ^= datanet_mbx.last_mbx_req_count);
1100                                                             /* try to service mailbox requests */
1101                call assign_sub_mbx (sub_mbx_no, subp);      /* find a free sub mbx */
1102                if sub_mbx_no = -1
1103                then return;                                 /* none available, will try again at next interrupt. */
1104                sub_mbx.io_cmd = rcd;                        /* set rcd in sub mailbox. */
1105                call send_mbx (sub_mbx_no);                  /* now ship submailbox off uncp. */
1106                datanet_mbx.last_mbx_req_count = datanet_mbx.last_mbx_req_count + 1;
1107                if datanet_mbx.last_mbx_req_count > MAX_MBX_REQ_CNT
1108                then datanet_mbx.last_mbx_req_count = 0;
1109                if ^sub_mbx_sent
1110                then call release_sub_mbx (sub_mbx_no);
1111           end;
1112      end /* spend_submailboxes */;
1113 ^L
1114 process_q:
1115      proc;
1116 
1117 /* process the queue of mailbox operations that could not be performed
1118    because no mailboxes wre available
1119 */
1120 
1121           q_first = fnp_info.cur_ptr;
1122           q_count = fnp_info.count;
1123           sub_mbx_no = 0;                                   /* preset mbx# for do while */
1124 
1125           do while (q_count > 0 & sub_mbx_no >= 0);
1126                call assign_sub_mbx (sub_mbx_no, subp);      /* find a free sub mbx */
1127                if sub_mbx_no >= 0                           /* now we can have one */
1128                then do;
1129                     qptr = ptr (ttybp, q_first);
1130                     if q_entry.pcb_offset ^= "0"b           /* for a specific channel */
1131                     then do;
1132                          pcbp = ptr (ttybp, q_entry.pcb_offset);
1133                          string (sub_mbx.line_number) = string (pcb.line_number);
1134                          devx = pcb.devx;
1135                     end;
1136                     else string (sub_mbx.line_number) = ""b;
1137 
1138                     if q_entry.opcode = accept_direct_output
1139                     then if pcb.dialed
1140                          then call process_send_output (sub_mbx_no, "0"b);
1141                          else ;
1142 
1143                     else do;
1144                          sub_mbx.io_cmd = wcd;
1145                          sub_mbx.op_code = q_entry.opcode;
1146                          sub_mbx.cmd_data_len = divide (q_entry.cmd_count, 6, 8, 0);
1147                          smbx_cmd_data_long = substr (q_entry.cmd_data, 1, q_entry.cmd_count);
1148                          call send_mbx (sub_mbx_no);
1149                          fnp_info.output_control_transactions = fnp_info.output_control_transactions + 1;
1150                     end;
1151 
1152                     if no_response                          /* give up in this case */
1153                     then goto update_q_ptrs;
1154 
1155                     q_first = q_entry.next;                 /* on to next queue entry */
1156                     q_count = q_count - 1;
1157                     call tty_space_man$free_space (size (q_entry), qptr);
1158                     if ^sub_mbx_sent
1159                     then call release_sub_mbx (sub_mbx_no);
1160                end;
1161 
1162                else fnp_info.mbx_unavailable = fnp_info.mbx_unavailable + 1;
1163           end;
1164 
1165 update_q_ptrs:
1166           fnp_info.cur_ptr = q_first;
1167           fnp_info.count = q_count;
1168           if q_count = 0
1169           then fnp_info.last_ptr = 0;
1170 
1171           return;
1172      end /* process_q */;
1173 ^L
1174 send_dial:
1175      proc;
1176           pcb.send_output = "1"b;
1177           devx = pcb.devx;
1178 
1179           if pcb.write_last ^= 0
1180           then do;
1181                blockp = ptr (ttybp, pcb.write_last);
1182                if buffer.turn
1183                then pcb.turn = "1"b;
1184                call throw_away_output;
1185           end;
1186 
1187           if pcb.turn = "1"b
1188           then do;
1189                call tty_space_man$get_chain (devx, 16, 1, INPUT, inchain_ptr);
1190                if inchain_ptr = null
1191                then do;
1192                     call syserr (SYSERR_announce, "uncp: special_dial, Failure of buffer to make '^a'", dial_name (ix));
1193                     call channel_manager$interrupt (devx, QUIT, ""b);
1194                     return;
1195                end;
1196                inchain = rel (inchain_ptr);
1197                blockp = inchain_ptr;
1198                numchars = EIGHT;
1199                bufp = addr (dial_name (ix));
1200                charp = addr (buffer.chars);
1201                charp -> chars = bufp -> chars;
1202                buffer.tally = numchars;
1203                rtx_info.break_char = "1"b;
1204                rtx_info.output_in_fnp = "0"b;
1205                rtx_info.output_in_ring_0 = "0"b;
1206                rtx_info.input_count = EIGHT;
1207                rtx_info.chain_head = inchain;
1208                rtx_info.chain_tail = rel (blockp);
1209                interrupt_info = unspec (rtx_info);
1210                call channel_manager$interrupt (devx, ACCEPT_INPUT, interrupt_info);
1211                pcb.baud_rate = BAUD_9600;
1212                pcb.dumpout = "1"b;
1213           end;
1214           call channel_manager$interrupt (devx, SEND_OUTPUT, ""b);
1215           return;
1216 
1217      end send_dial;
1218 ^L
1219 /* internal subroutine to process send output */
1220 
1221 process_send_output:
1222      proc (a_mbx_num, interrupt_entry);
1223 
1224 dcl  a_mbx_num fixed bin;                                   /* -1 indicates mailbox not already allocated */
1225 dcl  mbx_num fixed bin;
1226 dcl  interrupt_entry bit (1) aligned;                       /* indicates whether or not called on interrupt side */
1227 
1228           mbx_num = a_mbx_num;
1229 
1230           if pcb.end_frame | pcb.output_mbx_pending         /* if we're waiting for form-feed */
1231           then do;
1232                pcb.flags.send_output = "1"b;                /* we'll want output eventually */
1233                if pcb.turn = "1"b
1234                then call channel_manager$interrupt (devx, SEND_OUTPUT, ""b);
1235                                                             /* For the Quit problem */
1236                return;                                      /* don't do anything else */
1237           end;
1238 
1239           if pcb.enter_receive_pending | pcb.send_lf
1240           then do;
1241                sub_mbx.op_code = enter_receive;
1242                call send_mbx (mbx_num);
1243                return;
1244           end;
1245 
1246           if pcb.write_first = 0
1247           then do;
1248 
1249                pcb.flags.send_output = "1"b;                /* if no output then just set flag */
1250                call channel_manager$interrupt (devx, SEND_OUTPUT, ""b);
1251           end;
1252 
1253           else do;
1254                ix = pcb.baud_rate;
1255                if ix ^= BAUD_9600
1256                then do;
1257                     call send_dial;
1258                     return;
1259                end;
1260 
1261                if pcb.turn = "0"b
1262                then return;                                 /* Don't emit without the turn ON */
1263 
1264                if mbx_num = -1                              /* caller didn't supply one */
1265                then do;
1266                     call assign_sub_mbx (mbx_num, subp);    /* find a free sub mbx */
1267                     if mbx_num = -1                         /* still? we didn't get one */
1268                     then do;
1269                          call make_q_entry (accept_direct_output, 0, ""b);
1270                          fnp_info.mbx_unavailable = fnp_info.mbx_unavailable + 1;
1271                          return;                            /* we'll catch it later */
1272                     end;
1273                     else do;
1274                          string (sub_mbx.line_number) = string (pcb.line_number);
1275                     end;
1276                end;
1277 
1278 
1279                pcb.flags.send_output = "0"b;                /* make sure flag clear */
1280 
1281 
1282 /*                                                          no DCW with UNCP  */
1283 
1284                blockp = ptr (ttybp, pcb.write_first);       /* get ptr to buffer */
1285                if buffer.tally = 0                          /* we don't want this in a dcw */
1286                then call syserr (SYSERR_crash, "uncp: output buffer at ^o has zero tally", pcb.write_first);
1287 
1288 /*                                                          no DCW with UNCP    */
1289 
1290 
1291                pcb.write_first = buffer.next;               /* now bump to next buffer */
1292                pcb.write_cnt = pcb.write_cnt - buffer.tally;/* decrement count of chars in chain */
1293 
1294 
1295                if buffer.flags.end_of_page                  /* if this buffer fills a page/screen */
1296                then pcb.flags.end_frame = "1"b;             /* remember it */
1297 
1298 
1299                if pcb.write_first ^= 0
1300                then buffer.flags.turn = "0"b;               /* Give up turn if terminating  */
1301 
1302 
1303                sub_mbx.op_code = accept_direct_output;
1304                if buffer.flags.turn
1305                then pcb.flags.end_frame = "0"b;
1306                if buffer.flags.turn | buffer.flags.end_of_page
1307                then do;
1308                     sub_mbx.op_code = accept_last_output;
1309                     pcb.turn = "0"b;
1310                end;
1311 
1312                sub_mbx.command_data (1) = "0"b;             /* make sure it starts clean */
1313                sub_mbx.data_addr = bit (bin (bin (rel (blockp), 18) + tty_buf.absorig, 18), 18);
1314                j = bin (buffer.tally, 18);
1315                sub_mbx.word_cnt = 1 + divide (j + 3, 4, 18, 0);
1316                buffer.next = 0;                             /* indicate end of active write block */
1317                sub_mbx.command_data (3) = gateway_header;   /* save buffer header in command_data (3) */
1318                gateway_buf.cnt = j;
1319                gateway_buf.pad = "0"b;
1320                sub_mbx.io_cmd = wtx;                        /* set write text io command */
1321 
1322                if trace.buffer_out
1323                then call syserr (SYSERR_log, "uncp: buffer = ^v( ^w ^) ", sub_mbx.word_cnt, blockp -> buf_words);
1324 
1325                if pcb.connection_type = "01"b & sub_mbx.op_code = accept_direct_output
1326                then string (sub_mbx.line_number) =
1327                          string (addr (fnp_info.pcb_array_ptr -> pcb_array (pcb.uncp_pcbx)) -> pcb.line_number);
1328                pcb.output_mbx_pending = "1"b;
1329                call send_mbx (mbx_num);                     /* ship sub mbx off to 355 */
1330                fnp_info.output_data_transactions = fnp_info.output_data_transactions + 1;
1331                                                             /* meter */
1332                if pcb.write_first = 0
1333                then do;                                     /* see if we ran out of buffers */
1334                     pcb.write_last = 0;                     /* zero ptr to last */
1335                     if interrupt_entry
1336                     then call channel_manager$interrupt (devx, SEND_OUTPUT, ""b);
1337                                                             /* wakeup the user */
1338                end;
1339           end;
1340 
1341           return;                                           /* and return to caller */
1342      end;
1343 ^L
1344 /* internal procedure to respond to accept_input mailbox */
1345 
1346 process_accept_input:
1347      proc;
1348 
1349           input_count = fixed (substr (sub_mbx.command_data (1), 1, 18), 18) + 4;
1350                                                             /* get char count */
1351 
1352           j = divide (input_count + 3, 4, 17, 0);           /* compute number of words of circular buffer needed */
1353 
1354           do while (^stac (addr (uncp_buf.cq_lock), pds$processid));
1355                                                             /* lock the circular queue lock */
1356           end;
1357 
1358           k = uncp_buf.cq_max_size - uncp_buf.cq_next;
1359           if j <= uncp_buf.cq_free                          /* if there's space in the queue */
1360           then if enough_input_space (j)                    /* and buffers to spare */
1361                then do;
1362                     if j > k
1363                     then if j <= uncp_buf.cq_free - k
1364                          then uncp_buf.cq_next = 0;
1365                          else go to reject;
1366 
1367                     qorig = tty_buf.absorig + fixed (rel (addr (uncp_buf.circular_queue (0))));
1368                                                             /* get abs origin of circular buffer */
1369 
1370                     uncp_buf.cq_free = uncp_buf.cq_free - j;/* decrement count of free wds in circ buf */
1371 
1372                     if uncp_buf.circular_queue_size > MAX_FREE_BUFFERS
1373                     then do;                                /* getting too big */
1374                          uncp_buf.circular_queue_size = 0;  /* reset */
1375                          uncp_buf.queue_ave_cnt = 0;
1376                     end;
1377                     uncp_buf.circular_queue_size = uncp_buf.circular_queue_size +
1378                                                             /* update the ave size */
1379                          (uncp_buf.cq_max_size - uncp_buf.cq_free);
1380                                                             /* with current size */
1381                     uncp_buf.queue_ave_cnt = uncp_buf.queue_ave_cnt + 1;
1382                                                             /* bump q count */
1383 
1384                     uncp_buf.circular_queue (uncp_buf.cq_next) = 0;
1385                     sub_mbx.data_addr = bit (bin (qorig + uncp_buf.cq_next, 18), 18);
1386                                                             /* leave abs buffer addr for 355 */
1387                     uncp_buf.cq_next = uncp_buf.cq_next + j;/* compute new cb free area offset */
1388 
1389                     wrap_ptr = "0"b;                        /* clear wrap around ptr in sub mbx */
1390                     wrap_cnt = 0;                           /* and count in sub mbx */
1391 
1392                     if uncp_buf.cq_next > uncp_buf.cq_max_size
1393                     then do;                                /* we have wrap around */
1394                          wrap_ptr = bit (bin (qorig, 18), 18);
1395                                                             /* leave wrap around pointer in sub mbx */
1396                          uncp_buf.cq_next = uncp_buf.cq_next - uncp_buf.cq_max_size;
1397                                                             /* adjust free buffer offset */
1398                          wrap_cnt = input_count - 4 * (j - uncp_buf.cq_next);
1399                                                             /* leave wrap count in sub mbx */
1400                          sub_mbx.word_cnt = input_count - wrap_cnt;
1401                                                             /* and adjust word count */
1402                     end;
1403 
1404 
1405 
1406                     else sub_mbx.word_cnt = j;              /* words count */
1407 
1408                     if uncp_buf.cq_next = uncp_buf.cq_max_size
1409                     then uncp_buf.cq_next = 0;              /* exactly end of cb */
1410 
1411 
1412                     if trace.buffer_in
1413                     then call syserr (SYSERR_log, "uncp$process_accept_input: cq_next = ^w , cq_free = ^w , count = ^w ",
1414                               uncp_buf.cq_next, uncp_buf.cq_free, j);
1415 
1416                     sub_mbx.op_code = input_accepted;       /* inform 355 that we will take input now */
1417                     sub_mbx.io_cmd = rtx;
1418                     call return_mbx (sub_mbx_no);
1419                end;
1420 
1421                else go to reject;
1422 
1423           else do;
1424                uncp_buf.queue_full_cnt = uncp_buf.queue_full_cnt + 1;
1425                                                             /* bump q full count */
1426 reject:
1427                sub_mbx.io_cmd = wcd;
1428                sub_mbx.op_code = reject_request_temp;       /* inform 355 that we can not accept input
1429                                                                at the present time */
1430                call return_mbx (sub_mbx_no);
1431                fnp_info.input_reject_count = fnp_info.input_reject_count + 1;
1432                call channel_manager$interrupt (devx, INPUT_REJECTED, ""b);
1433           end;
1434 
1435           if ^stacq (uncp_buf.cq_lock, "0"b, pds$processid)
1436           then call syserr (SYSERR_crash, "uncp: inconsistent circular queue lock.");
1437 
1438      end /* process_accept_input */;
1439 ^L
1440 /* internal proc to process rtx */
1441 process_rtx:
1442      proc;
1443 
1444 dcl  (real_word_cnt, real_wrap_cnt) fixed bin;
1445 dcl  chars_to_move fixed bin;
1446 dcl  left_in_buffer fixed bin;
1447 dcl  char_array (0:numchars) char (1) unaligned based;
1448 
1449           sub_mbx.command_data (3) = "0"b;
1450 
1451           bufp = ptr (ttybp, bin (sub_mbx.data_addr, 24) - tty_buf.absorig);
1452           real_word_cnt = bufp -> unal_number;
1453           if real_word_cnt = 0
1454           then goto update_free;
1455 
1456           real_wrap_cnt = wrap_cnt;
1457           lcte.meters.in_bytes = lcte.meters.in_bytes + real_word_cnt;
1458 
1459           rtx_info.break_char = "1"b;
1460           call check_ff ("0"b);                             /* see if input ends with a form feed */
1461           input_count = real_word_cnt + real_wrap_cnt;
1462           if input_count ^= 0                               /* must have been a single FF that we discarded */
1463           then do;
1464 
1465                rtx_info.output_in_fnp = "0"b;
1466                rtx_info.output_in_ring_0 = (pcb.write_first ^= 0);
1467                rtx_info.input_count = input_count;
1468                if input_count > (tty_buf.bleft - abs_buf_limit) * 4
1469                                                             /* not now though */
1470                then go to no_input_space;
1471 
1472                nblocks = divide (input_count + bsizec - 1, bsizec, 17, 0);
1473                                                             /* figure out how many we'll need */
1474                call tty_space_man$get_chain (devx, 16, nblocks, INPUT, inchain_ptr);
1475                if inchain_ptr = null
1476                then do;
1477 no_input_space:
1478                     call syserr (SYSERR_announce,
1479                          "uncp: Unable to allocate input buffers for line ^o, input has been lost",
1480                          string (pcb.line_number));
1481                     call channel_manager$interrupt (devx, QUIT, ""b);
1482                                                             /* get the word to him somehow */
1483                     go to update_free;
1484                end;
1485                inchain = rel (inchain_ptr);
1486 
1487                bufp = ptr (ttybp, bin (sub_mbx.data_addr, 24) - tty_buf.absorig + 1);
1488                                                             /* get ptr to input buf */
1489 
1490                if trace.buffer_in
1491                then call syserr (SYSERR_log, "uncp: compte = ^w , buffer recu :  ^v( ^w ^) ", input_count,
1492                          divide (input_count + 3, 4, 17, 0), bufp -> buf_words);
1493 
1494                chars_left = real_word_cnt + real_wrap_cnt;
1495                blockp = inchain_ptr;                        /* pointer to first buffer */
1496                charp = addr (buffer.chars);
1497                chars_to_move = real_word_cnt;               /* up to end of circular buffer */
1498                left_in_buffer = bsizec;                     /* initially */
1499 
1500                do while (chars_left > 0);
1501                     numchars = min (left_in_buffer, chars_to_move);
1502                     charp -> chars = bufp -> chars;         /* chars is declared char (numchars) */
1503                     chars_left = chars_left - numchars;
1504                     chars_to_move = chars_to_move - numchars;
1505                     buffer.tally = buffer.tally + numchars;
1506 
1507                     if chars_left > 0                       /* there are more to do */
1508                     then do;
1509                          if chars_to_move <= 0              /* used up first set */
1510                          then if real_wrap_cnt > 0          /* are there any more? */
1511                               then do;
1512                                    bufp = addr (uncp_buf.circular_queue (0));
1513                                    chars_to_move = real_wrap_cnt;
1514                               end;
1515                               else ;
1516 
1517                          else bufp = addr (bufp -> char_array (numchars));
1518 
1519                          if buffer.tally = bsizec           /* buffer is full */
1520                          then do;
1521                               blockp = ptr (ttybp, buffer.next);
1522                                                             /* so move to next */
1523                               charp = addr (buffer.chars);
1524                               left_in_buffer = bsizec;
1525                          end;
1526 
1527                          else do;
1528                               charp = addr (charp -> char_array (numchars));
1529                               left_in_buffer = left_in_buffer - numchars;
1530                          end;
1531                     end;
1532                end;
1533 
1534                rtx_info.chain_head = inchain;
1535                rtx_info.chain_tail = rel (blockp);
1536                interrupt_info = unspec (rtx_info);
1537                call channel_manager$interrupt (devx, ACCEPT_INPUT, interrupt_info);
1538                pcb.turn = "1"b;
1539                if pcb.lfecho
1540                then pcb.send_lf = "1"b;
1541           end;
1542 
1543 /* the following statement must generate an ASQ instruction or there will be a locking problem */
1544 
1545 
1546 update_free:
1547           uncp_buf.cq_free = uncp_buf.cq_free + sub_mbx.word_cnt;
1548 
1549           call make_q_entry (accept_direct_output, 0, ""b);
1550 
1551           return;                                           /* and return to caller */
1552 ^L
1553 check_ff:
1554           proc (in_mbx);                                    /* internal procedure to check input for form-feed */
1555 
1556 dcl  in_mbx bit (1);
1557 dcl  wrapped bit (1);
1558 
1559                rtx_info.formfeed_present = "0"b;            /* for now */
1560                if pcb.sync_line                             /* form feeds not interesting in this case */
1561                then return;
1562                wrapped = "0"b;
1563 
1564 
1565 
1566                if wrap_ptr = "0"b
1567                then do;
1568                     bufp = ptr (ttybp, bin (sub_mbx.data_addr, 24) - tty_buf.absorig + 1);
1569                     chars_left = real_word_cnt;
1570                end;
1571 
1572 
1573                if pcb.extra_nl
1574                then substr (bufp -> input_chars, chars_left, 1) = "
1575 ";
1576 
1577                if substr (bufp -> input_chars, chars_left, 1) = form_feed
1578                                                             /* yup, input ends with FF */
1579                then rtx_info.formfeed_present = "1"b;
1580                if pcb.flags.end_frame & rtx_info.break_char /* time to restart suspended output */
1581                then do;
1582                     if (chars_left <= 2) & ^wrapped
1583                     then if verify (substr (bufp -> input_chars, 1, chars_left), ff_cr_lf) = 0
1584                          then do;                           /* this input is just to restart output, discard it */
1585                               if in_mbx
1586                               then numchars = 0;
1587                               else real_word_cnt = 0;
1588                          end;
1589                     pcb.flags.end_frame = "0"b;
1590                     pcb.turn = "1"b;
1591                     if pcb.enter_receive_pending
1592                     then call make_q_entry (accept_direct_output, 0, "0"b);
1593 
1594 
1595                     if pcb.flags.send_output                /* more output to ship */
1596                     then if pcb.write_first ^= 0            /* it's waiting in tty_buf */
1597                          then call make_q_entry (accept_direct_output, 0, ""b);
1598                                                             /* we'll get to it shortly */
1599                          else call channel_manager$interrupt (devx, SEND_OUTPUT, ""b);
1600                end;
1601 
1602           end /* check_ff */;
1603 
1604      end /* process_rtx */;
1605 ^L
1606 /* internal proc to check if this channel can have input space */
1607 
1608 enough_input_space:
1609      proc (count) returns (bit (1));
1610 
1611 dcl  count fixed bin;
1612 
1613           lctp = tty_buf.lct_ptr;
1614           chan_lctep = addr (lct.lcte_array (devx));
1615           return (chan_lctep -> lcte.input_words + count <= divide (tty_buf.bleft, input_bpart, 17, 0));
1616      end /* enough_input_space */;
1617 ^L
1618 
1619 /* internal proc to put an element onto delay queue */
1620 
1621 make_q_entry:
1622      proc (opc, cnt, databits);
1623 
1624 dcl  (opc, cnt) fixed bin (8),                              /* parameters */
1625      databits bit (8 * 36);
1626 
1627           call tty_space_man$get_space (size (q_entry), new_qp);
1628           if new_qp = null
1629           then do;
1630                call syserr (SYSERR_crash, "uncp: unable to allocate block for delay queue");
1631                return;
1632           end;
1633 
1634           if opc = accept_direct_output
1635           then pcb.flags.send_output = "0"b;                /* Correction for the untimely disconnects */
1636           new_qrel = bin (rel (new_qp));
1637           if fnp_info.cur_ptr = 0                           /* nothing in the queue yet */
1638           then fnp_info.cur_ptr = new_qrel;
1639           else do;
1640                qptr = ptr (ttybp, fnp_info.last_ptr);
1641                q_entry.next = new_qrel;                     /* make the preceding entry point to the new one */
1642           end;
1643 
1644           fnp_info.last_ptr = new_qrel;
1645           qptr = new_qp;
1646           fnp_info.count = fnp_info.count + 1;
1647           fnp_info.q_entries_made = fnp_info.q_entries_made + 1;
1648 
1649           q_entry.opcode = opc;                             /* set q element op code */
1650           q_entry.cmd_count = cnt;                          /* and command count */
1651           if pcbp ^= null ()
1652           then q_entry.pcb_offset = rel (pcbp);
1653           else q_entry.pcb_offset = "0"b;
1654           q_entry.next = 0;
1655           q_entry.cmd_data = databits;                      /* move data to q element */
1656           return;                                           /* return to caller */
1657      end;
1658 ^L
1659 /* internal procedure to derive devx & PCB pointer from mailbox line number */
1660 
1661 get_line_number:
1662      proc;
1663 
1664           pcbp = null;
1665           devx = -1;
1666 
1667           if string (sub_mbx.line_number) ^= "0"b
1668           then do;
1669                sub_mbx.line_number.is_hsla = "1"b;          /* with uncp every line is high speed */
1670                lano = sub_mbx.line_number.la_no;            /* get line adapter number for devx lookup */
1671                if sub_mbx.is_hsla
1672                then j = fnp_info.hsla_idx (fixed (lano));
1673                else j = fnp_info.lsla_idx (fixed (lano));   /* get starting position */
1674                do j = j to fnp_info.no_of_channels;         /* loop thru devx table */
1675                     pcbp = addr (fnp_info.pcb_array_ptr -> pcb_array (j));
1676                     if string (pcb.line_number) = string (sub_mbx.line_number)
1677                     then go to match;                       /* check for right slot */
1678                end;
1679                if (sub_mbx.io_cmd = rcd) & (sub_mbx.op_code = accept_new_terminal)
1680                then do;
1681                     devx = -1;
1682                     return;
1683                end;
1684                else if sub_mbx.io_cmd ^= wcd
1685                then do;
1686                     call syserr (SYSERR_beep, "uncp$interrupt: no slot number match for sub mbx ^o, FNP ^a", i,
1687                          substr ("abcdefgh", dno, 1));      /* bitch */
1688                     sub_mbx.io_cmd = 0;                     /* To force an error */
1689                end;
1690                return;
1691 match:
1692                devx = pcb.devx;                             /* copy devx to automatic */
1693                if pcb.connection_type = "01"b
1694                then do;
1695                     other_pcbp = addr (fnp_info.pcb_array_ptr -> pcb_array (pcb.uncp_pcbx));
1696                     devx = other_pcbp -> pcb.devx;
1697                end;
1698           end;
1699           return;
1700 
1701      end /* get_line_number */;
1702 ^L
1703 
1704 /* internal procedure to locate and assign a free sub mailbox */
1705 
1706 assign_sub_mbx:
1707      proc (a_sub_mbx_no, a_subp);
1708 
1709 dcl  a_sub_mbx_no fixed bin;
1710 dcl  a_subp ptr;
1711 dcl  sub_mbx_num fixed bin;
1712 
1713           sub_mbx_num = index (used_string, "0"b) - 1;
1714           if sub_mbx_num = -1
1715           then do;                                          /* none available */
1716                a_sub_mbx_no = -1;
1717                a_subp = null ();
1718                return;
1719           end;
1720 
1721           datanet_mbx.mbx_used_flags.used (sub_mbx_num) = "1"b;
1722                                                             /* set used flag */
1723           datanet_mbx.num_in_use = datanet_mbx.num_in_use + 1;
1724           fnp_info.max_mbx_in_use = max (fnp_info.max_mbx_in_use, datanet_mbx.num_in_use);
1725           fnp_info.cumulative_mbx_in_use = fnp_info.cumulative_mbx_in_use + datanet_mbx.num_in_use;
1726           fnp_info.mbx_in_use_updated = fnp_info.mbx_in_use_updated + 1;
1727           unspec (datanet_mbx.dn355_sub_mbxes (sub_mbx_num)) = "0"b;
1728           sub_mbx_sent = "0"b;
1729           a_sub_mbx_no = sub_mbx_num;
1730           a_subp = addr (datanet_mbx.dn355_sub_mbxes (sub_mbx_num));
1731           return;
1732      end assign_sub_mbx;
1733 %skip (3);
1734 /* internal procedure to release a sub mailbox when it wasn't really needed */
1735 
1736 release_sub_mbx:
1737      proc (a_sub_mbx_no);
1738 
1739 dcl  a_sub_mbx_no fixed bin;
1740 
1741           if a_sub_mbx_no < 0 | a_sub_mbx_no > 6
1742           then return;                                      /* not a valid mbx# */
1743           datanet_mbx.mbx_used_flags.used (a_sub_mbx_no) = "0"b;
1744                                                             /* reset used flag */
1745                                                             /* and decrement counters */
1746           fnp_info.cumulative_mbx_in_use = fnp_info.cumulative_mbx_in_use - datanet_mbx.num_in_use;
1747           fnp_info.mbx_in_use_updated = fnp_info.mbx_in_use_updated - 1;
1748           datanet_mbx.num_in_use = datanet_mbx.num_in_use - 1;
1749           return;
1750      end release_sub_mbx;
1751 ^L
1752 
1753 /* internal procedure to ship sub mbx off to 355 */
1754 return_mbx:
1755 send_mbx:
1756      proc (a_mbx_no);
1757 
1758 dcl  a_mbx_no fixed bin;
1759 dcl  mbx_no fixed bin;
1760 dcl  counter fixed bin;
1761 dcl  output_data_ptr ptr;
1762 dcl  output_data (3) bit (36) aligned based (output_data_ptr);
1763 dcl  1 ima aligned like io_manager_arg;
1764 
1765           mbx_no = a_mbx_no;
1766           if mbx_no = -1                                    /* caller running with local copy */
1767           then do;
1768                call assign_sub_mbx (mbx_no, subp);          /* find a free sub mbx */
1769                if mbx_no = -1                               /* OUCH! Should have had one! */
1770                then do;
1771                     call syserr (SYSERR_crash, "uncp: unable to re-assign fnp sub-mailbox.");
1772                     return;
1773                end;
1774                unspec (sub_mbx) = unspec (local_sub_mbx);   /* copy in local info */
1775                a_mbx_no = mbx_no;
1776           end;
1777 
1778           if ^fnp_info.io_manager_assigned
1779           then do;
1780                no_response = "1"b;                          /* lie, but effectively */
1781                return;
1782           end;
1783 
1784           do counter = 1 to LOOP_LIMIT while (datanet_mbx.dia_pcw.command ^= "0"b);
1785                                                             /* loop until dia picks up last command */
1786           end;
1787           if counter > LOOP_LIMIT                           /* it never did */
1788           then no_response = "1"b;
1789 
1790           else do;
1791                no_response = "0"b;
1792 
1793                if (mbx_no >= 0) & (mbx_no <= 6)
1794                then do;
1795                     sub_mbx.dn355_no = substr (bit (fnp_info.fnp_number), 7);
1796                     if sub_mbx.op_code = enter_receive
1797                     then do;
1798                          sub_mbx.io_cmd = wtx;
1799                          output_data_ptr = fnp_info.dcw_list_array_ptr;
1800                          output_data (*) = ""b;
1801 
1802                          sub_mbx.command_data (1) = "0"b;
1803                          sub_mbx.command_data (2) = "0"b;
1804                          sub_mbx.command_data (3) = "0"b;
1805                          if pcb.send_lf
1806                          then do;
1807                               sub_mbx.op_code = accept_direct_output;
1808                               unspec (output_data (2)) = "000001000000"b3;
1809                               unspec (output_data (3)) = "012000000000"b3;
1810                               sub_mbx.word_cnt = 2;
1811                               sub_mbx.data_addr =
1812                                    bit (bin (tty_buf.absorig + fixed (rel (addr (output_data (2)))), 18), 18);
1813                               pcb.send_lf = "0"b;
1814                          end;
1815                          else do;
1816                               sub_mbx.op_code = accept_last_output;
1817                               output_data (1) = "0"b;
1818                               sub_mbx.word_cnt = 1;
1819                               sub_mbx.data_addr =
1820                                    bit (bin (tty_buf.absorig + fixed (rel (addr (output_data (1)))), 18), 18);
1821                               pcb.enter_receive_pending, pcb.turn = "0"b;
1822                          end;
1823                          call get_line_number;
1824                          pcb.output_mbx_pending = "1"b;
1825                     end;
1826 
1827                     if sub_mbx.op_code = accept_last_output
1828                     then do;
1829                          call get_line_number;
1830                          if pcb.connection_type = "10"b
1831                          then do;
1832                               pcb.turn = "1"b;
1833                               sub_mbx.op_code = accept_direct_output;
1834                          end;
1835                     end;
1836 
1837                     if (sub_mbx.io_cmd = rtx) | (sub_mbx.io_cmd = wtx)
1838                     then sub_mbx.pad3 = (15)"0"b3 || "777"b3;
1839                end;
1840 
1841                sub_mbx.line_number.is_hsla = "0"b;          /*  for the line_number into uncp */
1842 
1843                if trace.mailbox
1844                then call syserr (SYSERR_log, "uncp$send_mbx: # ^o ^2( ^/ ^4( ^w ^) ^)", mbx_no, sub_mbx_array);
1845 
1846                string (datanet_mbx.dia_pcw) = initial_pcw;  /* initialize pcw */
1847                datanet_mbx.dia_pcw.mbx_no = bit (fixed (mbx_no + 1, 6), 6);
1848                                                             /* set sub mbx number */
1849 
1850                string (datanet_mbx.dia_pcw) = dn355_util$compute_parity (string (datanet_mbx.dia_pcw));
1851                                                             /* set the parity bit; bit 22 */
1852 
1853 
1854                ima.chx = fnp_info.io_manager_chx;
1855                ima.ptp = fnp_info.ptp;
1856                call io_manager$connect_direct (ima);        /* kick 355 */
1857                sub_mbx_sent = "1"b;                         /* show actual use of mbx */
1858 
1859                return;                                      /* return to caller */
1860           end;
1861      end send_mbx;
1862 ^L
1863 /* entry and internal proc to hangup all lines on an FNP */
1864 
1865 hangup_fnp_lines:
1866      entry (a_fnp_no);
1867 
1868 dcl  a_fnp_no fixed bin;
1869 
1870           infop = addr (dn355_data$);
1871           ttybp = addr (tty_buf$);
1872           uncpbp = datanet_info.uncp_bufp;
1873           call hangup_fnp (a_fnp_no);
1874           return;
1875 
1876 
1877 hangup_fnp:
1878      proc (fnp_no);
1879 
1880 dcl  fnp_no fixed bin;
1881 
1882           fnpp = addr (datanet_info.per_datanet (fnp_no));
1883           do j = 1 to fnp_info.no_of_channels;
1884                pcbp = addr (fnp_info.pcb_array_ptr -> pcb_array (j));
1885                if pcb.dialed
1886                then do;
1887                     call throw_away_output;
1888                     if pcb.connection_type ^= "10"b
1889                     then call channel_manager$interrupt ((pcb.devx), CRASH, ""b);
1890                end;
1891           end;
1892 
1893           if fnp_info.count > 0                             /* get rid of any outstanding delay queue entries */
1894           then do;
1895                q_count = fnp_info.count;
1896                q_first = fnp_info.cur_ptr;
1897                do q_count = q_count to 0 by -1 while (q_first ^= 0);
1898                     qptr = ptr (ttybp, q_first);            /* get real pointer to queue entry */
1899                     q_first = qptr -> q_entry.next;         /* save pointer to next one */
1900                     call tty_space_man$free_space (size (q_entry), qptr);
1901                end;
1902 
1903                fnp_info.count, fnp_info.cur_ptr, fnp_info.last_ptr = 0;
1904           end;
1905 
1906      end;
1907 
1908 
1909 
1910 throw_away_output:
1911      proc;
1912 
1913 /* throws away pending write chain on quit and hangup */
1914 
1915           if pcb.write_first ^= 0
1916           then do;
1917                call tty_space_man$free_chain ((pcb.devx), OUTPUT, ptr (ttybp, pcb.write_first));
1918                pcb.write_first, pcb.write_last, pcb.write_cnt = 0;
1919 
1920           end;
1921 
1922           pcb.end_frame = "0"b;
1923 
1924           return;
1925      end /* throw_away_output */;
1926 ^L
1927 /* internal procedure to report that DIA never set PCW to 0 */
1928 
1929 report_fnp_no_response:
1930      proc;
1931 
1932           call syserr (SYSERR_beep, "uncp: FNP ^a did not respond to mailbox interrupt", fnp_info.fnp_tag);
1933           call report_fnp_crash;                            /* treat it like a crash */
1934           return;
1935 
1936      end /* report_fnp_no_response */;
1937 
1938 
1939 /* internal procedure to tell initializer and clean up when FNP crashes */
1940 
1941 report_fnp_crash:
1942      proc;
1943 
1944           fnp_info.running = "0"b;                          /* it isn't any more */
1945 
1946 /*                  if fnp_info.dump_patch_in_progress       somebody's waiting for this */
1947 /*                  then call pxss$notify (FNP_DUMP_PATCH_EVENT);  don't let them wait forever */
1948 
1949           if ^fnp_info.bootloading                          /* if we weren't still loading it */
1950           then                                              /* now report hangups for all lines that were dialed to it */
1951                call hangup_fnp (dno);
1952           else fnp_info.bootloading = "0"b;
1953 
1954           auto_fnp_msg.state = FNP_DOWN;                    /* tell the responsible process */
1955           auto_fnp_msg.fnp_no = dno;
1956           auto_fnp_msg.flags = "0"b;
1957           unspec (fnp_event_message) = unspec (auto_fnp_msg);
1958           call pxss$ring_0_wakeup (fnp_info.boot_process_id, fnp_info.boot_ev_chan, fnp_event_message, 0);
1959 
1960           return;
1961 
1962      end report_fnp_crash;
1963 ^L
1964 purge_write_texte:
1965      proc;
1966           do i = 0 to 6;
1967                if timwb (i)
1968                then do;
1969                     subp = addr (datanet_mbx.dn355_sub_mbxes (i));
1970                     call get_line_number;
1971                     if sub_mbx.io_cmd = wtx
1972                     then do;
1973                          if pcb.output_mbx_pending & sub_mbx.command_data (3) ^= "0"b
1974                          then do;
1975                               da = bin (sub_mbx.data_addr, 18) - tty_buf.absorig;
1976                               blockp = ptr (ttybp, da);     /* set ptr to buffer */
1977                               if pcb.connection_type = "10"b
1978                               then pcbp = addr (fnp_info.pcb_array_ptr -> pcb_array (pcb.uncp_pcbx));
1979                               gateway_header = sub_mbx.command_data (3);
1980                               call tty_space_man$free_chain ((pcb.devx), OUTPUT, blockp);
1981                          end;
1982                     end;
1983                     if sub_mbx.io_cmd = rtx
1984                     then if sub_mbx.word_cnt ^= 0
1985                          then do;
1986                               uncp_buf.cq_free = uncp_buf.cq_free + sub_mbx.word_cnt;
1987                               sub_mbx.word_cnt = 0;
1988                          end;
1989                     datanet_mbx.mbx_used_flags.used (i) = "0"b;
1990                     datanet_mbx.num_in_use = datanet_mbx.num_in_use - 1;
1991                end;
1992           end;
1993           return;
1994      end;                                                   /* purge_write_texte */
1995 ^L
1996 check_lock:
1997      proc;
1998 
1999 /* the cleanup procedure -- makes sure we don't crawl out with lock set */
2000 
2001           if queue_locked
2002           then call syserr (SYSERR_crash, "uncp: attempted crawlout with FNP queue locked");
2003 
2004           else if masked
2005           then call pmut$unwire_unmask (wire_arg, wire_ptr);/* it's probably too late, but just in case */
2006 
2007           return;
2008      end check_lock;
2009 %page;
2010 
2011 /* Begin message documentation invisible
2012 
2013    *  This documentation lacks the standard token heading but rather uses
2014    *  lowercase and the keyword invisible so that the messages documented
2015    *  below will not be included in the standard error message documentation
2016    *  shipment.
2017 
2018    Message:
2019    uncp: invalid interrupt level N
2020 
2021    S:  $beep
2022 
2023    T:  $run
2024 
2025    M:  An FNP interrupt has been received with an invalid interrupt level of
2026    octal value N and will be ignored.  If this message is displayed when a
2027    DN6670 is being powered up, this message can be ignored.  If this message
2028    occurs under any other circumstances, there might be something wrong with
2029    the system's interface with the FNP and should be investigated by FE
2030    representatives.
2031 
2032    A:  $inform
2033 
2034 
2035    Message:
2036    uncp: emergency interrupt from FNP X: FAULT
2037    .br
2038    FNP instruction counter = IC
2039    .br
2040    channel CHN, fault status = FS
2041    .br
2042    FNP_MODULE: REASON_FOR_CRASH
2043 
2044    S:  $beep
2045 
2046    T:  $run
2047 
2048    M:  An emergency interrupt has been received from FNP X indicating
2049    it has crashed.  All lines dialed to FNP X will be hung up.  The
2050    crash was nominally caused by a fault of type FAULT.  Lines
2051    following the first line of the message appear only in certain cases
2052    and provide additional information about the nature of the crash.
2053 
2054    A:  The system will automatically attempt to reboot the crashed FNP.
2055    Subsequent messages will indicate the success or failure of this attempt.
2056    No action is required now, but action may be required if the
2057    automatic reboot fails.
2058 
2059 
2060    Message:
2061    uncp$interrupt: no slot number match for sub mbx N, FNP X
2062 
2063    S:  $beeper
2064 
2065    T:  $run
2066 
2067    M:  An error has occurred processing submailbox N for FNP X.
2068    The submailbox indicates a line number for which no match could
2069    be found.
2070 
2071    A:  $inform
2072 
2073 
2074    Message:
2075    uncp: Message from FNP X: MESSAGE
2076 
2077    S:  $info
2078 
2079    T:  $run
2080 
2081    M:  An error has been detected by FNP X as explained by MESSAGE.
2082 
2083    A:  No action is required by the operator to deal with the error mentioned
2084    in the message.  Action may be required by appropriate personnel to correct
2085    the problem that caused the error and undo what the FNP may have done to
2086    continue operation.  This may require shutting down the FNP for repairs by
2087    Field Engineering and reboot of the FNP to restore full operation.
2088 
2089 
2090    Message:
2091    uncp$interrupt: unrecognized op code OPCODE with rcd from FNP X for devx N
2092 
2093    S:  $beeper
2094 
2095    T:  $run
2096 
2097    M:  An invalid op code, OPCODE, has been received from FNP X for device
2098    index N in a mailbox containing an rcd (read control data) command.
2099 
2100    A:  $inform
2101 
2102 
2103    Message:
2104    uncp$interrupt: unrecognized io command from FNP X for line N
2105 
2106    S:  $beeper
2107 
2108    T:  $run
2109 
2110    M:  An invalid io command was received from FNP X for line N.
2111 
2112    A:  $inform
2113 
2114 
2115    Message:
2116    uncp: output buffer at N has zero tally
2117 
2118    S:  $crash
2119 
2120    T:  $run
2121 
2122    M:  An output buffer with a zero tally has been found at offset N
2123    in the segment tty_buf.
2124 
2125    A:  $inform
2126 
2127 
2128    Message:
2129    uncp: unable to allocate block for delay queue
2130 
2131    S:  $crash
2132 
2133    T:  $run
2134 
2135    M: There was insufficient space left in tty_buf to allocate a block
2136    in which to build a delay queue.
2137 
2138    A:  $inform
2139 
2140 
2141    Message:
2142    FNP X did not respond to mailbox interrupt
2143 
2144    S:  $beep
2145 
2146    T:  $run
2147 
2148    M:  An attempt to interrupt FNP X was unsuccessful. The FNP is assumed
2149    to be down.
2150 
2151    A:  The system will automatically attempt to reboot the crashed FNP.
2152    Subsequent messages will indicate the success or failure of this attempt.
2153    No action is required now, but action may be required if the
2154    automatic reboot fails.
2155 
2156 
2157    Message:
2158    uncp: inconsistent queue lock
2159 
2160    S:  $crash
2161 
2162    T:  $run
2163 
2164    M:  A process attempted to unlock the interrupt queue lock without having it
2165    locked.
2166 
2167    A:  $inform
2168 
2169 
2170    Message:
2171    uncp: LCTE lock ^= processid
2172 
2173    S:     $crash
2174 
2175    T:     $run
2176 
2177    M:  The FNP channel lock did not contain the processid of the process
2178    attempting to unlock it.
2179 
2180 
2181    Message:
2182    uncp: attempted crawlout with FNP queue locked
2183 
2184    S:     $crash
2185 
2186    T:     $run
2187 
2188    M:     An attempt was made to crawl out while an FNP queue lock (a processor
2189    lock) was locked.
2190 
2191    A:     $inform
2192 
2193 
2194    Message:
2195    uncp$interrupt: line number of 0 with non-global opcode in submbx N, FNP X
2196 
2197    S:  $beeper
2198 
2199    T:  $run
2200 
2201    M:  Mailbox N from FNP X contained a non-global opcode which requires a
2202    non-zero line number.
2203 
2204 
2205    Message:
2206    uncp: unable to re-assign fnp sub-mailbox.
2207 
2208    S:     $crash
2209 
2210    T:     $run
2211 
2212    M:     During interrupt processing the sub-mailbox from the FNP is
2213    copied to local storage and the sub-mbx freed.  As part of handling the
2214    interrupt a need to return information to the FNP was found.  However the
2215    sub-mbx could not be re-assigned.
2216    $err
2217 
2218    A:     $inform
2219    $recover
2220 
2221    End message documentation invisible */
2222 
2223 
2224      end uncp;