1 /****^  ***********************************************************
   2         *                                                         *
   3         * Copyright, (C) Honeywell Bull Inc., 1987                *
   4         *                                                         *
   5         * Copyright, (C) Honeywell Information Systems Inc., 1982 *
   6         *                                                         *
   7         * Copyright (c) 1972 by Massachusetts Institute of        *
   8         * Technology and Honeywell Information Systems, Inc.      *
   9         *                                                         *
  10         *********************************************************** */
  11 
  12 
  13 /* format: style4,insnl,delnl,^ifthendo */
  14 dn355:
  15      procedure;
  16           return;                                           /* should never be called here */
  17 
  18 /*        Date last modified and reason
  19 
  20    Written 10/08/74 by F. A. Canali for new tty dim
  21    Modified by Robert Coren and Mike Grady to fix bugs and add features
  22    Modified by Robert Coren 10/08/75 for multiple 355s
  23    Modified by J. Stern 04/22/77 to introduce WTCBs
  24    Modified by J. Stern 06/23/77 to behave correctly when no submailboxes available
  25    Modified by J. Stern 07/28/77 to use all 3 words of command data in submailbox
  26    Modified Jan.-Feb. 1978 to use variable-size output buffers and fix some bugs
  27    Modified 3/13/78 by Robert Coren to use clock builtin instead of clock_ and to get correct
  28    time at hangup_fnp_lines entry
  29    Modified August 1978 by Robert Coren for demultiplexing
  30    Modified November 8, 1978 by Robert Coren to introduce FNP-initiated mailboxes
  31    Modified July 2 1979 by B. Greenberg for negotiated echo.
  32    Modified March 1980 by Robert Coren to eliminate use of circular buffer.
  33    Modified April 1980 by Robert Coren to add metering information.
  34    Modified 1980 December by Art Beattie to ignore interrupts in invalid levels.  Also allowed command_data for
  35    send_wcd operations to be 216 bits long (6 * 36-bit word).  Corrected error message documentation.
  36    Modified December 1980 by Robert Coren to handle report_meters opcode
  37    Modified April 1981 by Chris Jones for io_manager conversion
  38    Modified September 1981 by Robert Coren to record character counts in LCTE meters and to force COLTS buffer size to maximum
  39    Modified February 1982 by C. Hornig for MR10 io_manager.
  40    Modified June 1982 by Robert Coren to process "line_masked" opcode.
  41    Modified 1984-07-28 BIM for dn355_boot_interrupt$system_fault.
  42    Modified 1984-07-30 BIM for paged mode IOM.
  43    Modified September 1984 by Robert Coren to use include file to define delay queue entries
  44 */
  45 
  46 /****^  HISTORY COMMENTS:
  47   1) change(86-04-23,Coren), approve(86-04-23,MCR7300),
  48      audit(86-06-19,Beattie), install(86-07-08,MR12.0-1089):
  49      To handle 8-word echo-break tables.
  50   2) change(86-06-19,Kissel), approve(86-07-30,MCR7475), audit(86-09-04,Coren),
  51      install(86-10-09,MR12.0-1181):
  52      Changed to support the new tty event message format declared in
  53      net_event_message.incl.pl1 which replaces tty_event_message.incl.pl1.
  54   3) change(87-07-20,Farley), approve(88-02-24,MCR7791),
  55      audit(88-03-09,Beattie), install(88-03-15,MR12.2-1035):
  56      Changed no response loop to use a real time constant and retry the timeout
  57      or error one time along with reporting the problem.  If no response occurs
  58      after the retry then the FNP will be crashed.
  59                                                    END HISTORY COMMENTS */
  60 %page;
  61 interrupt:
  62      entry (x_dno, x_level, x_status);                      /* entry from iom_manager */
  63 
  64 dcl  x_dno fixed bin (35);                                  /* index from assignment time */
  65 dcl  x_level fixed bin (3);                                 /* interrupt level */
  66 dcl  x_status bit (36) aligned;                             /* status after special or fault */
  67 
  68           if tc_data$system_shutdown ^= 0
  69           then return;                                      /* ignore 355's if shut down in progress */
  70           interrupt_entry = "1"b;
  71 
  72           call setup;
  73           level = x_level;                                  /* copy level to local stack */
  74           if datanet_info.trace
  75           then do;
  76                if level ^= 3 | ^fnp_info.running
  77                then syserr_severity = just_tell;
  78                else syserr_severity = log;
  79                call syserr (syserr_severity,
  80                     "dn355: FNP ^a level ^d status ^w^[ running^]^[ bootloading^]^[ t_and_d_in_progress^]", fnp_name,
  81                     level, x_status, fnp_info.running, fnp_info.bootloading, fnp_info.t_and_d_in_progress);
  82           end;
  83           if level ^= 3 & level ^= 7
  84           then do;                                          /* if not a good interrupt level */
  85                call syserr (beeper, "dn355: FNP ^a invalid interrupt level ^o", fnp_name, level);
  86                if fnp_info.bootloading
  87                then if level = 1                            /* system fault */
  88                     then call dn355_boot_interrupt$system_fault (dno);
  89                return;                                      /* lets hope its benign */
  90           end;
  91 
  92           if (^fnp_info.t_and_d_in_progress) & (^fnp_info.running) & (^fnp_info.bootloading)
  93           then return;                                      /* spurious interrupt */
  94 
  95           if ^stac (addr (lcte.lock), pds$processid)        /* somebody else has it */
  96           then do;
  97 
  98                do while (^stac (addr (fnp_info.queue_lock), pds$processid));
  99                end;
 100 
 101                if level = 7
 102                then fnp_info.level_7_pending = "1"b;
 103                else fnp_info.level_3_pending = "1"b;
 104 
 105                if stac (addr (lcte.lock), pds$processid)    /* in case it got unlocked meanwhile */
 106                then call process_int_queue ("0"b);
 107 
 108                else if ^stacq (fnp_info.queue_lock, "0"b, pds$processid)
 109                then call syserr (crash_system, "dn355: inconsistent queue lock");
 110           end;
 111 
 112           else do;
 113                call process_int (level);
 114 
 115                do while (^stac (addr (fnp_info.queue_lock), pds$processid));
 116                end;                                         /* check the queue to see if anything came in while we had the lock */
 117 
 118                call process_int_queue ("0"b);
 119           end;
 120 
 121           return;
 122 
 123 global_exit:                                                /* if abort out of an internal proc */
 124           if interrupt_entry
 125           then do;
 126                if stacq (lcte.lock, "0"b, pds$processid)    /* make sure we undo anything we did */
 127                then if lcte.notify_reqd
 128                     then do;
 129                          lcte.notify_reqd = "0"b;
 130                          call pxss$notify (tty_ev);
 131                     end;
 132 
 133                lcte.locked_for_interrupt = "0"b;
 134 
 135           end;
 136           return;
 137 ^L
 138 /* entry to send a command to the FNP */
 139 send_wcd:
 140      entry (a_fnpp, a_pcbp, opa, chrsa, data);
 141 
 142 dcl  a_fnpp ptr,                                            /* parameters */
 143      a_pcbp ptr,
 144      opa fixed bin (8),
 145      data bit (*),
 146      chrsa fixed bin (8);                                   /* numeric */
 147 
 148 dcl  tdata bit (8 * 36);                                    /* could be up to 8 words for set_echnego_break_table */
 149 dcl  data_len fixed bin (8);
 150 
 151           pcbp = a_pcbp;
 152           go to send_join;
 153 
 154 send_global_wcd:
 155      entry (a_fnpp, opa, chrsa, data);
 156 
 157           pcbp = null ();
 158 
 159 send_join:
 160           interrupt_entry = "0"b;
 161           fnpp = a_fnpp;
 162           ttybp = addr (tty_buf$);                          /* get ptr to tty buf */
 163           infop = addr (dn355_data$);                       /* and dn 355 info */
 164           lctep = fnp_info.lcte_ptr;
 165           operation = opa;                                  /* and copy op to local stack */
 166           if ^fnp_info.running                              /* can't talk to it if it's not listening */
 167           then return;
 168 
 169           no_response = "0"b;
 170           dno = fnp_info.fnp_number;
 171           mbxp = fnp_info.mbx_pt;                           /* get pointer to mailbox */
 172           data_len = min (length (tdata), chrsa);           /* compute bit length of command data */
 173           if data_len > 0
 174           then tdata = substr (data, 1, data_len);
 175           else tdata = "0"b;
 176 
 177           i = index (used_string, "0"b);                    /* find a free sub mbx */
 178           if i = 0                                          /* no submailbox */
 179           then do;
 180                call make_q_entry (operation, data_len, tdata);
 181                fnp_info.mbx_unavailable = fnp_info.mbx_unavailable + 1;
 182                                                             /* form q entry element from data */
 183           end;
 184 
 185           else do;                                          /* we have a sub mbx, ship it off to the 355 */
 186                subp = addr (datanet_mbx.dn355_sub_mbxes (i - 1));
 187                                                             /* get sub mbx addr */
 188                if pcbp ^= null ()
 189                then do;
 190                     string (sub_mbx.line_number) = string (pcb.line_number);
 191                                                             /* move line number to sub mbx */
 192                     devx = pcb.devx;
 193                end;
 194 
 195                else string (sub_mbx.line_number) = "0"b;    /* unless no pcb (global call) */
 196 
 197                sub_mbx.op_code = operation;                 /* set sub mbx op */
 198                sub_mbx.cmd_data_len = divide (data_len, 6, 17, 0);
 199                                                             /* set data length */
 200                if operation = accept_direct_output          /* if output op */
 201                then do;
 202                     if ^pcb.flags.dialed                    /* output without a dialup? */
 203                     then call throw_away_output;            /* discard it */
 204 
 205                     else call process_send_output (i - 1, "0"b);
 206                end;
 207 
 208                else if operation = set_echnego_break_table
 209                then do;
 210                     if pcb.flags.dialed
 211                     then call send_echo_table (i - 1, tdata);
 212                end;
 213 
 214                else do;
 215                     sub_mbx.io_cmd = wcd;                   /* set write control data cmd */
 216                     smbx_cmd_data_long = substr (tdata, 1, data_len);
 217                                                             /* move command data to sub mbx */
 218                     call send_mbx (i - 1);                  /* ship the mbx off to the 355 */
 219                     fnp_info.output_control_transactions = fnp_info.output_control_transactions + 1;
 220                end;
 221 
 222 
 223                if no_response
 224                then call report_fnp_no_response;
 225           end;
 226 
 227 
 228           return;                                           /* return to caller */
 229 ^L
 230 process_interrupt_queue:
 231      entry (x_dno);
 232 
 233           interrupt_entry = "0"b;
 234           call setup;
 235           on cleanup call check_lock;
 236           masked = "1"b;                                    /* have to mask and wire while holding queue lock */
 237           call pmut$wire_and_mask (wire_arg, wire_ptr);
 238 
 239           do while (^stac (addr (fnp_info.queue_lock), pds$processid));
 240           end;
 241           queue_locked = "1"b;
 242 
 243           call process_int_queue ("1"b);
 244           return;
 245 ^L
 246 setup:
 247      proc;
 248 
 249           ttybp = addr (tty_buf$);                          /* get addr of tty buffer segment */
 250           dno = x_dno;                                      /* copy 355 number to local stack */
 251           infop = addr (dn355_data$);                       /* get address of 355 info segment */
 252 
 253           fnpp = addr (datanet_info.per_datanet (dno));
 254           fnp_name = fnp_info.fnp_tag;
 255           mbxp = fnp_info.mbx_pt;                           /* get mailbox pointer */
 256           lctep = fnp_info.lcte_ptr;
 257 
 258           return;
 259      end setup;
 260 ^L
 261 process_int_queue:
 262      proc (caller_masked);
 263 
 264 /* called with queue locked. Empties the queue, and must unlock it when done */
 265 
 266 dcl  caller_masked bit (1);                                 /* indicates whether caller explicitly called pmut$wire_and_mask */
 267 
 268           do while (dequeue (level));
 269                fnp_info.processed_from_q = fnp_info.processed_from_q + 1;
 270                                                             /* meter */
 271                if ^stacq (fnp_info.queue_lock, "0"b, pds$processid)
 272                then call syserr (crash_system, "dn355: inconsistent queue lock");
 273 
 274                queue_locked = "0"b;
 275                if caller_masked
 276                then call pmut$unwire_unmask (wire_arg, wire_ptr);
 277                masked = "0"b;
 278 
 279                call process_int (level);
 280 
 281                if caller_masked
 282                then do;                                     /* if we unmasked, we have to mask again */
 283                     masked = "1"b;
 284                     call pmut$wire_and_mask (wire_arg, wire_ptr);
 285                end;
 286 
 287                do while (^stac (addr (fnp_info.queue_lock), pds$processid));
 288                end;
 289                queue_locked = "1"b;
 290           end;
 291 
 292           lcte.locked_for_interrupt = "0"b;
 293           if ^stacq (lcte.lock, "0"b, pds$processid)
 294           then call syserr (crash_system, "dn355: LCTE lock ^^= processid");
 295 
 296           if ^stacq (fnp_info.queue_lock, "0"b, pds$processid)
 297           then call syserr (crash_system, "dn355: inconsistent queue lock");
 298 
 299           queue_locked = "0"b;
 300           if caller_masked
 301           then call pmut$unwire_unmask (wire_arg, wire_ptr);
 302           masked = "0"b;
 303 
 304           if lcte.notify_reqd
 305           then do;
 306                lcte.notify_reqd = "0"b;
 307                call pxss$notify (tty_ev);
 308           end;
 309 
 310           return;
 311 
 312 dequeue:
 313           proc (a_level) returns (bit (1));
 314 
 315 dcl  a_level fixed bin;
 316 
 317                if fnp_info.level_3_pending
 318                then do;
 319                     fnp_info.level_3_pending = "0"b;
 320                     a_level = 3;
 321                     return ("1"b);
 322                end;
 323 
 324                else if fnp_info.level_7_pending
 325                then do;
 326                     fnp_info.level_7_pending = "0"b;
 327                     a_level = 7;
 328                     return ("1"b);
 329                end;
 330 
 331                else return ("0"b);
 332 
 333           end /* dequeue */;
 334      end /* process_int_queue */;
 335 ^L
 336 process_int:
 337      proc (a_level);
 338 
 339 /* internal procedure to process an interrupt, either when it occurs or from the queue */
 340 
 341 dcl  a_level fixed bin;
 342 
 343           level = a_level;
 344 
 345           lcte.locked_for_interrupt = "1"b;
 346           if level = 7
 347           then do;                                          /* emergency interrupt */
 348 
 349                if fnp_info.t_and_d_in_progress
 350                then do;
 351                     if fnp_info.t_and_d_lev_7_occurred
 352                     then return;
 353                     fnp_info.t_and_d_lev_7_occurred = "1"b;
 354 t_and_d_join:
 355                     if fnp_info.t_and_d_notify_requested
 356                     then do;
 357                          call pxss$notify (tty_ev);
 358                          fnp_info.t_and_d_notify_requested = "0"b;
 359                     end;
 360                     unspec (auto_net_event_message) = "0"b;
 361                     auto_net_event_message.version = NET_EVENT_MESSAGE_VERSION_1;
 362                     auto_net_event_message.network_type = MCS_NETWORK_TYPE;
 363                     auto_net_event_message.handle = dno;
 364                     auto_net_event_message.type = level;
 365                     unspec (net_event_message_arg) = unspec (auto_net_event_message);
 366                     call pxss$unique_ring_0_wakeup (fnp_info.boot_process_id, fnp_info.boot_ev_chan,
 367                          net_event_message_arg, 0);
 368                     return;
 369                end;
 370 
 371 /* figure out reason for crash according to data in mailbox header */
 372 
 373                fault_type = datanet_mbx.crash_data.fault_code;
 374                if fault_type > hbound (dn355_messages$fault_names, 1) | fault_type < 0
 375                then fault_name = "unknown fault";
 376                else fault_name = dn355_messages$fault_names (fault_type);
 377 
 378                call syserr (beeper, "dn355: emergency interrupt from FNP ^a: ^a", fnp_info.fnp_tag, fault_name);
 379 
 380                if datanet_mbx.crash_data.ic ^= 0
 381                then call syserr (just_tell, "FNP instruction counter = ^6o", datanet_mbx.crash_data.ic);
 382 
 383                if fault_type = iom_channel_fault
 384                then call syserr (just_tell, "channel ^o, fault status = ^6o", datanet_mbx.crash_data.fault_word,
 385                          datanet_mbx.crash_data.iom_fault_status);
 386 
 387                else if fault_type = illegal_opcode
 388                then if dn355_word.opcode = die_code         /* did 355 crash deliberately? */
 389                     then do;
 390                          modulep = addr (dn355_messages$per_module);
 391                          module_num = fixed (dn355_word.modnum, 4);
 392                          if module_num > 0 & module_num <= hbound (dn355_modules.list_offset, 1)
 393                               & dn355_word.crash_code > 0 & dn355_word.crash_code <= hbound (modulep -> message_offset, 1)
 394                          then do;
 395                               reasonp = ptr (modulep, dn355_modules.list_offset (module_num));
 396                               reasonp = ptr (reasonp, reasonp -> message_offset (dn355_word.crash_code));
 397 
 398                               call syserr (just_tell, "^a: ^a", dn355_modules.name (module_num), dn355_reason.msg);
 399                          end;
 400                     end;
 401 
 402                call report_fnp_crash;                       /* report it and hang up lines */
 403                return;                                      /* done with this interrupt */
 404           end;
 405 
 406 /* level must be 3, a normal everyday 355 interrupt */
 407 
 408           if fnp_info.bootloading                           /* if this is bootload status */
 409           then do;
 410                call dn355_boot_interrupt (dno);             /* let special routine figure it out */
 411                return;
 412           end;
 413 
 414           if fnp_info.t_and_d_in_progress
 415           then do;
 416                if fnp_info.t_and_d_lev_3_occurred
 417                then return;
 418                fnp_info.t_and_d_lev_3_occurred = "1"b;
 419                go to t_and_d_join;
 420           end;
 421 
 422           if ^fnp_info.running                              /* if this interrupt is premature, ignore it */
 423           then return;
 424 
 425           no_response = "0"b;                               /* initially */
 426 
 427           if fnp_info.count > 0                             /* had we had to wait for a free mbx? */
 428           then call process_q;
 429 ^L
 430 /* process any submailboxes which have been returned by the 355 */
 431 
 432           timw = ldac (addr (datanet_mbx.term_inpt_mpx_wd));/* get timw and clear */
 433 
 434           do i = 0 to 7;                                    /* loop over submailbox indicators */
 435 
 436                if timwb (i) & ^no_response
 437                then do;                                     /* if mailbox was returned by 355 then we have something to do */
 438 
 439                     subp = addr (datanet_mbx.dn355_sub_mbxes (i));
 440                                                             /* get pointer to sub mailbox */
 441                     datanet_mbx.mbx_used_flags.used (i) = "0"b;
 442                                                             /* clear submailbox used flag */
 443                     datanet_mbx.num_in_use = datanet_mbx.num_in_use - 1;
 444                     fnp_info.cumulative_mbx_in_use = fnp_info.cumulative_mbx_in_use + datanet_mbx.num_in_use;
 445                     fnp_info.mbx_in_use_updated = fnp_info.mbx_in_use_updated + 1;
 446 
 447                     if sub_mbx.io_cmd = wcd
 448                     then do;
 449                          if sub_mbx.op_code = dump_mem | sub_mbx.op_code = patch_mem
 450                          then do;
 451                               fnp_info.dump_patch_in_progress = "0"b;
 452                               call pxss$notify (FNP_DUMP_PATCH_EVENT);
 453                          end;
 454 
 455                          else if sub_mbx.op_code = report_meters
 456                          then do;
 457                               call get_line_number;
 458                               if devx = -1
 459                               then if fnp_info.get_meters_waiting
 460                                                             /* fnp_multiplexer is waiting for global meters */
 461                                    then do;
 462                                         fnp_info.get_meters_waiting = "0"b;
 463                                         call pxss$notify (FNP_METER_EVENT);
 464                                    end;
 465                                    else ;                   /* copy_meters for whole FNP shouldn't arise */
 466 
 467                               else if pcb.get_meters_waiting/* waiting for channel's meters */
 468                               then do;
 469                                    pcb.get_meters_waiting = "0"b;
 470                                    call pxss$notify (FNP_METER_EVENT);
 471                               end;
 472 
 473                               else pcb.copied_meters_ready = "1"b;
 474                                                             /* must be copy_meters, mark it so call side can copy them to unwired */
 475                          end;
 476                     end;                                    /* just free submbx */
 477 
 478                     else do;
 479                          call get_line_number;
 480                          if sub_mbx.io_cmd = wtx
 481                          then do;                           /* check for write text */
 482 
 483                               pcb.output_mbx_pending = "0"b;
 484                               dcwlptr = addr (fnp_info.dcw_list_array_ptr -> dcw_list_array (i));
 485                               chain_head_ptr = ptr (ttybp, bin (dcw_list (1).dcw_ptr, 18) - (tty_buf.absorig + dataoff));
 486                               call tty_space_man$free_chain ((pcb.devx), OUTPUT, chain_head_ptr);
 487                                                             /* and the output chain */
 488 
 489                               if sub_mbx.command_data (1) ^= "0"b
 490                                                             /* immediate send-output */
 491                               then call process_send_output (i, "1"b);
 492 
 493                          end;
 494 
 495 
 496 
 497                          else do;
 498                               call syserr (beeper, "dn355: unrecognized io command ^o from FNP ^a for line ^o",
 499                                    sub_mbx.io_cmd, fnp_info.fnp_tag, bin (string (sub_mbx.line_number), 10));
 500                                                             /* complain */
 501                               call report_fnp_crash;        /* act as if FNP crashed */
 502                               return;
 503                          end;
 504                     end;
 505                end;
 506           end;
 507 
 508           do i = 8 to 11;                                   /* now look at FNP-initiated mailboxes */
 509                if timwb (i) & ^no_response
 510                then do;
 511                     subp = addr (datanet_mbx.fnp_sub_mbxes (i - 8));
 512                     call get_line_number;
 513 
 514                     if sub_mbx.io_cmd = rcd
 515                     then do;                                /* check for control stuff */
 516 
 517                          if (sub_mbx.op_code = accept_direct_input) | (sub_mbx.op_code = send_output)
 518                               | (sub_mbx.op_code = input_in_mailbox)
 519                          then do;
 520                               fnp_info.bleft_355 = fnp_sub_mbx.n_free_buffers - 4;
 521                                                             /* get the buffer count from 355 */
 522 
 523                               if fnp_info.bleft_355 < 0
 524                               then                          /* if above was too much correction */
 525                                    fnp_info.bleft_355 = 0;  /* make it safe */
 526 
 527 
 528                               if fnp_info.free_size > 16000000000
 529                               then do;
 530                                    fnp_info.free_size = 0;
 531                                    fnp_info.free_count = 0;
 532                               end;
 533 
 534                               fnp_info.free_size = fnp_info.free_size + fnp_info.bleft_355;
 535                               fnp_info.free_count = fnp_info.free_count + 1;
 536                          end;
 537 
 538                          if sub_mbx.op_code = accept_direct_input | sub_mbx.op_code = input_in_mailbox
 539                          then fnp_info.input_data_transactions = fnp_info.input_data_transactions + 1;
 540                          else fnp_info.input_control_transactions = fnp_info.input_control_transactions + 1;
 541 
 542                          if sub_mbx.op_code = accept_new_terminal
 543                          then do;                           /* check for new terminal on line */
 544 
 545 
 546                               pcb.line_type, dialup_info.line_type = bin (sub_mbx.command_data (1), 17);
 547                               if sub_mbx.command_data (2)
 548                               then pcb.baud_rate = baud_table (bin (sub_mbx.command_data (2), 17));
 549 
 550                               do j = 1 to n_sync_line_types while (sync_line_type (j) ^= pcb.line_type);
 551                               end;
 552                               pcb.sync_line = (j <= n_sync_line_types);
 553 
 554                               if ^pcb.sync_line             /* asynchronous */
 555                               then bits_per_char = 10;
 556                               else bits_per_char = 8;       /* assumption for synchronous lines */
 557 
 558                               max_buf_chars =
 559                                    divide (divide (pcb.baud_rate, bits_per_char, 17, 0), buf_per_second, 17, 0);
 560                               pcb.max_buf_size = min (16 * divide (max_buf_chars + 67, 64, 17, 0), 128);
 561                                                             /* round up to multiple of 16 words */
 562                               if pcb.line_type = LINE_COLTS
 563                               then pcb.max_buf_size = 128;  /* COLTS channel always gets big buffers */
 564 
 565                               dialup_info.baud_rate = pcb.baud_rate;
 566                               dialup_info.max_buf_size = pcb.max_buf_size;
 567                               dialup_info.buffer_pad = 0;
 568                               dialup_info.receive_mode_device = (dialup_info.line_type = LINE_ETX);
 569                               dialup_info.pad = "0"b;
 570                               pcb.dialed = "1"b;
 571 
 572                               sub_mbx.op_code = terminal_accepted;
 573                                                             /* inform 355 that term is ok */
 574                               sub_mbx.cmd_data_len = 3;     /* we will put write buffer threshold in command data */
 575                               if ^pcb.high_speed            /* less than 1200 baud */
 576                               then addr (sub_mbx.command_data) -> unal_number = 2;
 577                                                             /* set low write buffer threshold */
 578                               else addr (sub_mbx.command_data) -> unal_number = 4;
 579                                                             /* set high write buffer threshold */
 580                               sub_mbx.io_cmd = wcd;
 581                               call return_mbx (i);
 582                               interrupt_info = unspec (dialup_info);
 583                               call channel_manager$interrupt (devx, DIALUP, interrupt_info);
 584 
 585                          end;
 586 
 587 
 588                          else if sub_mbx.op_code = disconnected_line
 589                          then do;                           /* see if line just hung up */
 590                               pcb.dialed = "0"b;
 591                               call throw_away_output;
 592                               call channel_manager$interrupt (devx, HANGUP, ""b);
 593                               call free_mbx (i);
 594 
 595                          end;
 596 
 597                          else if sub_mbx.op_code = wru_timeout
 598                          then do;                           /* 355 couldn't get answerback */
 599                               if pcb.dialed
 600                               then call channel_manager$interrupt (devx, WRU_TIMEOUT, ""b);
 601                               call free_mbx (i);
 602                          end;
 603 
 604                          else if sub_mbx.op_code = break_condition
 605                          then do;                           /* check for break */
 606 
 607                               if pcb.dialed
 608                               then do;
 609                                    if pcb.hndlquit
 610                                    then call throw_away_output;
 611                                    call channel_manager$interrupt (devx, QUIT, ""b);
 612                               end;
 613                               call free_mbx (i);
 614                          end;
 615 
 616 
 617                          else if sub_mbx.op_code = send_output
 618                          then do;                           /* is this request for output? */
 619 
 620                               call free_mbx (i);
 621                               if pcb.dialed
 622                               then call process_send_output (-1, "1"b);
 623                                                             /* -1 indicates no current mailbox */
 624                          end;
 625 
 626 
 627                          else if sub_mbx.op_code = accept_direct_input
 628                          then do;                           /* check for input from terminal */
 629 
 630                               if pcb.dialed
 631                               then call process_accept_input;
 632 
 633                               else do;
 634                                    sub_mbx.io_cmd = wcd;    /* we'll tell him to hang up */
 635                                    sub_mbx.op_code = disconnect_this_line;
 636                                    call return_mbx (i);
 637                               end;
 638                          end;
 639 
 640                          else if sub_mbx.op_code = error_message
 641                          then do;                           /* error message from 355 */
 642                               offset = bin (error_msg.data (1), 18);
 643                                                             /* get which error message this is */
 644                               if offset > 0 & offset <= hbound (dn355_messages$error_messages, 1)
 645                               then do;
 646                                    offset = dn355_messages$error_messages (offset);
 647                                                             /* offset of message */
 648                                    reasonp = addr (dn355_messages$error_messages);
 649                                                             /* get ptr */
 650                                    reasonp = ptr (reasonp, offset);
 651                                                             /* now we have message */
 652                                    reason_msg = dn355_reason.msg;
 653                               end;
 654 
 655                               else reason_msg = "unrecognized error ^o ^o ^o";
 656 
 657                               do ix = 1 to 3;
 658                                    full_words (ix) = bin (error_msg.data (ix + 1), 18);
 659                               end;
 660                               call syserr (just_tell, "dn355: Message from FNP ^a: " || reason_msg, fnp_info.fnp_tag,
 661                                    full_words);
 662                               call free_mbx (i);
 663                          end;
 664 
 665                          else if sub_mbx.op_code = input_in_mailbox
 666                          then do;
 667                               if pcb.dialed
 668                               then call process_input_in_mbx;
 669                               else do;
 670                                    sub_mbx.io_cmd = wcd;    /* tell him to give up */
 671                                    sub_mbx.op_code = disconnect_this_line;
 672                                    call return_mbx (i);
 673                               end;
 674                          end;
 675 
 676                          else if sub_mbx.op_code >= first_acu_op_code & sub_mbx.op_code <= last_acu_op_code
 677                          then do;                           /* acu failure */
 678                               interrupt_info = bit (bin (sub_mbx.op_code, 9));
 679                               call channel_manager$interrupt (devx, DIAL_STATUS, interrupt_info);
 680                               call free_mbx (i);
 681                          end;
 682 
 683                          else if sub_mbx.op_code = line_status
 684                          then do;                           /* some status from fnp */
 685                               interrupt_info = substr (unspec (sub_mbx.command_data), 1, 72);
 686                               call channel_manager$interrupt (devx, LINE_STATUS, interrupt_info);
 687                               call free_mbx (i);
 688                          end;
 689 
 690                          else if sub_mbx.op_code = ack_echnego_init
 691                          then do;
 692                               call free_mbx (i);
 693                               call channel_manager$interrupt (devx, ACKNOWLEDGE_ECHNEGO_INIT, "0"b);
 694                          end;
 695 
 696                          else if sub_mbx.op_code = ack_echnego_stop
 697                          then do;
 698                               call free_mbx (i);
 699                               call channel_manager$interrupt (devx, ACKNOWLEDGE_ECHNEGO_STOP, "0"b);
 700                          end;
 701 
 702                          else if sub_mbx.op_code = line_masked
 703                          then do;                           /* see if channel was masked */
 704                               pcb.dialed, pcb.listen = "0"b;
 705                               call throw_away_output;
 706                               call syserr (just_tell,
 707                                    "dn355: FNP masked channel ^a.h^d^[0^;^]^d for excessive interrupts", fnp_info.fnp_tag,
 708                                    binary (sub_mbx.line_number.la_no, 3), (binary (sub_mbx.line_number.slot_no, 6) < 10),
 709                                    binary (sub_mbx.line_number.slot_no, 6));
 710                               call channel_manager$interrupt (devx, MASKED, ""b);
 711                               call free_mbx (i);
 712 
 713                          end;
 714 
 715                          else do;
 716                               call syserr (beeper, "dn355: unrecognized op code ^o with rcd from FNP ^a for devx ^o",
 717                                    sub_mbx.op_code, fnp_info.fnp_tag, devx);
 718                                                             /* someone goofed */
 719                               call report_fnp_crash;
 720                               return;
 721                          end;
 722                     end;
 723 
 724 
 725 
 726                     else if sub_mbx.io_cmd = rtx
 727                     then call process_rtx;                  /* check for read text */
 728 
 729                     else do;
 730                          call syserr (beeper, "dn355: unrecognized io command ^o from FNP ^a for line ^o", sub_mbx.io_cmd,
 731                               fnp_info.fnp_tag, bin (string (sub_mbx.line_number), 10));
 732                                                             /* complain */
 733                          call report_fnp_crash;             /* give up on this FNP */
 734                          return;
 735                     end;
 736                end;
 737           end;
 738 
 739 
 740           if ^no_response                                   /* assuming we believe FNP is still there */
 741           then if fnp_info.count > 0
 742                then call process_q;
 743 
 744 
 745           if no_response                                    /* if someone discovered that the FNP was gone */
 746           then call report_fnp_no_response;
 747 
 748           return;
 749      end /* process_int */;
 750 ^L
 751 process_q:
 752      proc;
 753 
 754 /* process the queue of mailbox operations that could not be performed
 755    because no mailboxes wre available
 756 */
 757 
 758           q_first = fnp_info.cur_ptr;
 759           q_count = fnp_info.count;
 760           i = 1;                                            /* preset while variable */
 761 
 762           do while (q_count > 0 & i > 0);
 763                i = index (used_string, "0"b);
 764                if i > 0                                     /* now we can have one */
 765                then do;
 766                     subp = addr (datanet_mbx.dn355_sub_mbxes (i - 1));
 767                     qptr = ptr (ttybp, q_first);
 768                     if q_entry.pcb_offset ^= "0"b           /* for a specific channel */
 769                     then do;
 770                          pcbp = ptr (ttybp, q_entry.pcb_offset);
 771                          string (sub_mbx.line_number) = string (pcb.line_number);
 772                          devx = pcb.devx;
 773                     end;
 774                     else string (sub_mbx.line_number) = ""b;
 775 
 776                     if q_entry.opcode = accept_direct_output
 777                     then if pcb.dialed
 778                          then call process_send_output (i - 1, "0"b);
 779                          else ;
 780 
 781                     else if q_entry.opcode = set_echnego_break_table
 782                     then if pcb.dialed
 783                          then call send_echo_table (i - 1, q_entry.cmd_data);
 784                          else ;
 785 
 786                     else do;
 787                          sub_mbx.io_cmd = wcd;
 788                          sub_mbx.op_code = q_entry.opcode;
 789                          sub_mbx.cmd_data_len = divide (q_entry.cmd_count, 6, 8, 0);
 790                          smbx_cmd_data_long = substr (q_entry.cmd_data, 1, q_entry.cmd_count);
 791                          call send_mbx (i - 1);
 792                          fnp_info.output_control_transactions = fnp_info.output_control_transactions + 1;
 793                     end;
 794 
 795                     if no_response                          /* give up in this case */
 796                     then go to update_q_ptrs;
 797 
 798                     q_first = q_entry.next;                 /* on to next queue entry */
 799                     q_count = q_count - 1;
 800                     call tty_space_man$free_space (size (q_entry), qptr);
 801                end;
 802 
 803                else fnp_info.mbx_unavailable = fnp_info.mbx_unavailable + 1;
 804           end;
 805 
 806 update_q_ptrs:
 807           fnp_info.cur_ptr = q_first;
 808           fnp_info.count = q_count;
 809           if q_count = 0
 810           then fnp_info.last_ptr = 0;
 811 
 812           return;
 813      end /* process_q */;
 814 ^L
 815 /* internal subroutine to process send output */
 816 
 817 process_send_output:
 818      proc (a_mbx_num, interrupt_entry);
 819 
 820 dcl  a_mbx_num fixed bin;                                   /* -1 indicates mailbox not already allocated */
 821 dcl  mbx_num fixed bin;
 822 dcl  interrupt_entry bit (1) aligned;                       /* indicates whether or not called on interrupt side */
 823 
 824           mbx_num = a_mbx_num;
 825           if pcb.end_frame | pcb.output_mbx_pending         /* if we're waiting for form-feed  or we got delayed */
 826           then do;
 827                pcb.flags.send_output = "1"b;                /* we'll want output eventually */
 828                return;                                      /* don't do anything else */
 829           end;
 830 
 831           if pcb.write_first = 0
 832           then do;
 833                pcb.flags.send_output = "1"b;                /* if no output then just set flag */
 834                call channel_manager$interrupt (devx, SEND_OUTPUT, ""b);
 835           end;
 836 
 837           else do;
 838                if mbx_num = -1                              /* caller didn't supply one */
 839                then do;
 840                     mbx_num = index (used_string, "0"b) - 1;/* find a free one */
 841 
 842                     if mbx_num = -1                         /* still? we didn't get one */
 843                     then do;
 844                          call make_q_entry (accept_direct_output, 0, ""b);
 845                          fnp_info.mbx_unavailable = fnp_info.mbx_unavailable + 1;
 846                          return;                            /* we'll catch it later */
 847                     end;
 848                     else do;
 849                          subp = addr (datanet_mbx.dn355_sub_mbxes (mbx_num));
 850                          string (sub_mbx.line_number) = string (pcb.line_number);
 851                     end;
 852                end;
 853 
 854 
 855                pcb.flags.send_output = "0"b;                /* make sure flag clear */
 856                dcwlptr = addr (fnp_info.dcw_list_array_ptr -> dcw_list_array (mbx_num));
 857 
 858                sub_mbx.data_addr = bit (bin (bin (rel (dcwlptr), 18) + tty_buf.absorig, 18), 18);
 859                output_limit =
 860                     max (
 861                     min (divide ((fnp_info.bleft_355 - tc_data$fnp_buffer_threshold) * 60, output_bpart, 17, 0),
 862                     max_chain_len * 4 * (pcb.max_buf_size - 1)), 1);
 863 
 864                output_chars = 0;                            /* none so far */
 865                continue = "1"b;
 866                do j = 1 to max_chain_len while (pcb.write_first ^= 0 & output_chars < output_limit & continue);
 867                                                             /* set up dcw list */
 868                     dcw_list (j).dcw_ptr = bit (bin (pcb.write_first + dataoff + tty_buf.absorig, 18), 18);
 869                                                             /* set dcw abs addr */
 870                     blockp = ptr (ttybp, pcb.write_first);  /* get ptr to buffer */
 871                     if buffer.tally = 0                     /* we don't want this in a dcw */
 872                     then call syserr (crash_system, "dn355: output buffer at ^o has zero tally", pcb.write_first);
 873 
 874                     dcw_list (j).dcw_tally = bit (buffer.tally, 9);
 875                                                             /* set dcw tally from buffer */
 876                     dcw_list (j).pad = "0"b;                /* 355 depends on this */
 877                     pcb.write_first = buffer.next;          /* now bump to next buffer */
 878                     pcb.write_cnt = pcb.write_cnt - buffer.tally;
 879                                                             /* decrement count of chars in chain */
 880                     output_chars = output_chars + buffer.tally;
 881                                                             /* keep count of characters sent */
 882                     if buffer.flags.end_of_page             /* if this buffer fills a page/screen */
 883                     then do;
 884                          pcb.flags.end_frame = "1"b;        /* remember it */
 885                          continue = "0"b;                   /* terminate the loop */
 886                     end;
 887                end;
 888 
 889                chain_len = max (j - 1, 1);                  /* this is now the length of the chain */
 890 
 891                sub_mbx.word_cnt = chain_len;                /* we have maximum length dcw list */
 892                sub_mbx.op_code = accept_direct_output;      /* and do not have last buffer */
 893                sub_mbx.command_data (1) = "0"b;             /* make sure it starts clean */
 894                sub_mbx.io_cmd = wtx;                        /* set write text io command */
 895                pcb.output_mbx_pending = "1"b;
 896                buffer.next = 0;                             /* indicate end of active write block */
 897                call send_mbx (mbx_num);                     /* ship sub mbx off to 355 */
 898                fnp_info.output_data_transactions = fnp_info.output_data_transactions + 1;
 899                lcte.meters.out_bytes = lcte.meters.out_bytes + output_chars;
 900                                                             /* meter */
 901 
 902                if /* tree */ pcb.write_first = 0
 903                then do;                                     /* see if we ran out of buffers */
 904                     pcb.write_last = 0;                     /* zero ptr to last */
 905                     if interrupt_entry
 906                     then call channel_manager$interrupt (devx, SEND_OUTPUT, ""b);
 907                                                             /* wakeup the user */
 908                end;
 909                else if chain_len < max_chain_len & ^pcb.flags.end_frame
 910                                                             /* must have stopped because there wasn't enough space */
 911                     then fnp_info.fnp_space_restricted_output = fnp_info.fnp_space_restricted_output + 1;
 912           end;
 913 
 914           return;                                           /* and return to caller */
 915      end;
 916 ^L
 917 /* internal subroutine to process set_echnego_break_table operation */
 918 
 919 /* Because the echo table is 8 words long, it won't fit in a sub_mbx, so we
 920    have to send the FNP the address so it can read the table. To avoid extra
 921    storage overhead, and the necessity of freeing storage when the operation
 922    completes, the table is put in the dcw_list area corresponding to the mailbox. */
 923 
 924 send_echo_table:
 925      procedure (mbx_num, table_bits);
 926 
 927 dcl  mbx_num fixed bin;
 928 dcl  table_bits bit (8 * 36);
 929 
 930 dcl  table_ptr pointer;
 931 dcl  bits_to_send bit (8 * 36) based;
 932 
 933           if ^pcb.dialed
 934           then return;
 935           table_ptr = addr (fnp_info.dcw_list_array_ptr -> dcw_list_array (mbx_num));
 936           table_ptr -> bits_to_send = table_bits;
 937           sub_mbx.op_code = set_echnego_break_table;
 938           sub_mbx.io_cmd = wcd;
 939           sub_mbx.data_addr = bit (bin (bin (rel (table_ptr), 18) + tty_buf.absorig, 18), 18);
 940           sub_mbx.word_cnt = 8;
 941 
 942           call send_mbx (mbx_num);
 943           fnp_info.output_control_transactions = fnp_info.output_control_transactions + 1;
 944           return;
 945      end send_echo_table;
 946 ^L
 947 /* internal procedure to respond to accept_input mailbox */
 948 
 949 process_accept_input:
 950      proc;
 951 
 952 dcl  tally fixed bin;
 953 dcl  buf_size fixed bin;
 954 dcl  prev_blockp ptr;
 955 
 956           input_count = input_sub_mbx.n_chars;              /* get char count */
 957           j = divide (input_count + 3, 4, 17, 0);           /* compute number of words of circular buffer needed */
 958 
 959           if enough_input_space (j) & pcb.read_first = 0
 960           then do;
 961                do k = 1 to input_sub_mbx.n_buffers;
 962                     tally = input_sub_mbx.dcw (k).tally;
 963                     buf_size = 16 * divide (tally + 67, 64, 17, 0);
 964                                                             /* get next higher multiple of 16 words */
 965                     call tty_space_man$get_buffer (devx, buf_size, INPUT, blockp);
 966                     if blockp = null ()                     /* couldn't get the space */
 967                     then do;
 968                          if pcb.read_first ^= 0             /* if we started building a chain */
 969                          then call tty_space_man$free_chain (devx, INPUT, ptr (ttybp, pcb.read_first));
 970                          pcb.read_first = 0;
 971                          go to reject;
 972                     end;
 973 
 974                     if pcb.read_first = 0
 975                     then pcb.read_first = bin (rel (blockp));
 976                     else prev_blockp -> buffer.next = bin (rel (blockp));
 977 
 978                     buffer.tally = tally;
 979                     input_sub_mbx.dcw (k).abs_addr =
 980                          bit (bin (tty_buf.absorig + bin (rel (addr (buffer.chars))), 24), 24);
 981                                                             /* point DCW at data portion of buffer */
 982                     prev_blockp = blockp;
 983                end;
 984 
 985                pcb.read_last = bin (rel (blockp));
 986 
 987                sub_mbx.op_code = input_accepted;            /* inform 355 that we will take input now */
 988                sub_mbx.io_cmd = rtx;
 989                call return_mbx (i);
 990           end;
 991 
 992           else do;
 993 reject:
 994                sub_mbx.io_cmd = wcd;
 995                sub_mbx.op_code = reject_request_temp;       /* inform 355 that we can not accept input
 996                                                                at the present time */
 997                call return_mbx (i);
 998                fnp_info.input_reject_count = fnp_info.input_reject_count + 1;
 999                call channel_manager$interrupt (devx, INPUT_REJECTED, ""b);
1000           end;
1001 
1002      end /* process_accept_input */;
1003 ^L
1004 /* internal proc to process rtx */
1005 process_rtx:
1006      proc;
1007 
1008 dcl  real_word_cnt fixed bin;
1009 dcl  n_words fixed bin;
1010 dcl  buf_size fixed bin;
1011 dcl  source_ptr ptr;
1012 dcl  target_ptr ptr;
1013 
1014           fnp_info.input_data_transactions = fnp_info.input_data_transactions + 1;
1015           real_word_cnt = input_sub_mbx.n_chars;
1016           lcte.meters.in_bytes = lcte.meters.in_bytes + real_word_cnt;
1017           rtx_info.break_char = substr (input_sub_mbx.command_data, 18, 1);
1018           call check_ff ("0"b);                             /* see if input ends with a form feed */
1019           input_count = real_word_cnt;
1020           if input_count ^= 0                               /* must have been a single FF that we discarded */
1021           then do;
1022                rtx_info.output_in_fnp = substr (input_sub_mbx.command_data, 17, 1);
1023                rtx_info.output_in_ring_0 = (pcb.write_first ^= 0);
1024                rtx_info.input_count = input_count;
1025                rtx_info.chain_head = bit (pcb.read_first, 18);
1026                rtx_info.chain_tail = bit (pcb.read_last, 18);
1027                interrupt_info = unspec (rtx_info);
1028                call channel_manager$interrupt (devx, ACCEPT_INPUT, interrupt_info);
1029           end;
1030 
1031           else call tty_space_man$free_chain (devx, INPUT, ptr (ttybp, pcb.read_first));
1032 
1033           pcb.read_first, pcb.read_last = 0;
1034 
1035           call free_mbx (i);
1036 
1037           return;                                           /* and return to caller */
1038 ^L
1039 process_input_in_mbx:
1040      entry;
1041 
1042 /* we will copy input directly from mailbox into one buffer (if possible) */
1043 
1044           numchars = fnp_sub_mbx.n_chars;
1045           rtx_info.break_char = substr (fnp_sub_mbx.command_data, 18, 1);
1046           call check_ff ("1"b);
1047           if numchars > 0
1048           then do;
1049                n_words = divide (numchars + 3, 4, 17, 0);
1050                if enough_input_space (n_words)
1051                then do;
1052                     buf_size = 16 * (divide (n_words + 17, 16, 17, 0));
1053                                                             /* get next multiple of 16 words */
1054                     call tty_space_man$get_buffer (devx, buf_size, INPUT, blockp);
1055                     if blockp = null
1056                     then go to not_enough_space;
1057 
1058                     source_ptr = addr (fnp_sub_mbx.input_data);
1059                     target_ptr = addr (buffer.chars);
1060                     target_ptr -> chars = source_ptr -> chars;
1061                     buffer.tally = numchars;
1062                     rtx_info.output_in_fnp = substr (fnp_sub_mbx.command_data, 17, 1);
1063                     rtx_info.output_in_ring_0 = (pcb.write_first ^= 0);
1064                     rtx_info.input_count = numchars;
1065                     rtx_info.chain_head, rtx_info.chain_tail = rel (blockp);
1066                                                             /* only one buffer */
1067                     lcte.meters.in_bytes = lcte.meters.in_bytes + numchars;
1068                     interrupt_info = unspec (rtx_info);
1069                     call channel_manager$interrupt (devx, ACCEPT_INPUT, interrupt_info);
1070                     call free_mbx (i);
1071                end;
1072 
1073                else do;                                     /* space test failed */
1074 not_enough_space:
1075                     sub_mbx.io_cmd = wcd;                   /* tell him we can't take it */
1076                     sub_mbx.op_code = reject_request_temp;
1077                     call return_mbx (i);
1078                     call channel_manager$interrupt (devx, INPUT_REJECTED, ""b);
1079                end;
1080           end;
1081 
1082           else call free_mbx (i);                           /* nothing there except form_feed */
1083           return;
1084 ^L
1085 check_ff:
1086           proc (in_mbx);                                    /* internal procedure to check input for form-feed */
1087 
1088 dcl  in_mbx bit (1);
1089 
1090                rtx_info.formfeed_present = "0"b;            /* for now */
1091                if pcb.sync_line                             /* form feeds not interesting in this case */
1092                then return;
1093 
1094                if in_mbx
1095                then do;
1096                     bufp = addr (fnp_sub_mbx.input_data);
1097                     chars_left = numchars;
1098                end;
1099 
1100                else do;
1101                     blockp = ptr (ttybp, pcb.read_last);
1102                     chars_left = buffer.tally;
1103                     bufp = addr (buffer.chars);
1104                end;
1105 
1106                if substr (bufp -> input_chars, chars_left, 1) = form_feed
1107                                                             /* yup, input ends with FF */
1108                then rtx_info.formfeed_present = "1"b;
1109                if pcb.flags.end_frame & rtx_info.break_char /* time to restart suspended output */
1110                then do;
1111                     if (chars_left <= 2)
1112                     then if verify (substr (bufp -> input_chars, 1, chars_left), ff_cr_lf) = 0
1113                          then do;                           /* this input is just to restart output, discard it */
1114                               if in_mbx
1115                               then numchars = 0;
1116                               else real_word_cnt = 0;
1117                          end;
1118                     pcb.flags.end_frame = "0"b;
1119                     if pcb.flags.send_output                /* more output to ship */
1120                     then if pcb.write_first ^= 0            /* it's waiting in tty_buf */
1121                          then call make_q_entry (accept_direct_output, 0, ""b);
1122                                                             /* we'll get to it shortly */
1123                          else call channel_manager$interrupt (devx, SEND_OUTPUT, ""b);
1124                end;
1125 
1126           end /* check_ff */;
1127 
1128      end /* process_rtx */;
1129 ^L
1130 /* internal proc to check if this channel can have input space */
1131 
1132 enough_input_space:
1133      proc (count) returns (bit (1));
1134 
1135 dcl  count fixed bin;
1136 
1137           lctp = tty_buf.lct_ptr;
1138           chan_lctep = addr (lct.lcte_array (devx));
1139           return (chan_lctep -> lcte.input_words + count <= divide (tty_buf.bleft, input_bpart, 17, 0));
1140      end /* enough_input_space */;
1141 ^L
1142 
1143 /* internal proc to put an element onto delay queue */
1144 
1145 make_q_entry:
1146      proc (opc, cnt, databits);
1147 
1148 dcl  (opc, cnt) fixed bin (8),                              /* parameters */
1149      databits bit (8 * 36);
1150 
1151           call tty_space_man$get_space (size (q_entry), new_qp);
1152           if new_qp = null
1153           then do;
1154                call syserr (crash_system, "dn355: unable to allocate block for delay queue");
1155                return;
1156           end;
1157 
1158           new_qrel = bin (rel (new_qp));
1159           if fnp_info.cur_ptr = 0                           /* nothing in the queue yet */
1160           then fnp_info.cur_ptr = new_qrel;
1161           else do;
1162                qptr = ptr (ttybp, fnp_info.last_ptr);
1163                q_entry.next = new_qrel;                     /* make the preceding entry point to the new one */
1164           end;
1165 
1166           fnp_info.last_ptr = new_qrel;
1167           qptr = new_qp;
1168           fnp_info.count = fnp_info.count + 1;
1169           fnp_info.q_entries_made = fnp_info.q_entries_made + 1;
1170 
1171           q_entry.opcode = opc;                             /* set q element op code */
1172           q_entry.cmd_count = cnt;                          /* and command count */
1173           if pcbp ^= null ()
1174           then q_entry.pcb_offset = rel (pcbp);
1175           else q_entry.pcb_offset = "0"b;
1176           q_entry.next = 0;
1177           q_entry.cmd_data = databits;                      /* move data to q element */
1178           return;                                           /* return to caller */
1179      end;
1180 ^L
1181 /* internal procedure to derive devx & PCB pointer from mailbox line number */
1182 
1183 get_line_number:
1184      proc;
1185 
1186 dcl  x fixed bin;
1187 
1188           if string (sub_mbx.line_number) = "0"b
1189           then do;                                          /* some type of global request */
1190                do x = 1 to hbound (global_opcodes, 1) while (sub_mbx.op_code ^= global_opcodes (x));
1191                end;                                         /* make sure it really is */
1192                if x > hbound (global_opcodes, 1)
1193                then do;                                     /* else can the FNP */
1194                     call syserr (beeper, "dn355: line number of 0 with non-global opcode in submbx ^o, FNP ^a", i,
1195                          fnp_info.fnp_tag);
1196                     call report_fnp_crash;
1197                     go to global_exit;
1198                end;
1199 
1200                pcbp = null;
1201                devx = -1;
1202           end;
1203 
1204           else do;
1205                n_pcbs = fnp_info.no_of_channels;
1206                if string (sub_mbx.line_number) = TANDD_LINE_NUMBER
1207                                                             /* don't decode this, go straight to it */
1208                then pcbp = addr (fnp_info.pcb_array_ptr -> pcb_array (fnp_info.tandd_pcbx));
1209                else do;
1210                     lano = sub_mbx.line_number.la_no;       /* get line adapter number for devx lookup */
1211                     if sub_mbx.is_hsla
1212                     then j = fnp_info.hsla_idx (fixed (lano));
1213                     else j = fnp_info.lsla_idx (fixed (lano));
1214                                                             /* get starting position */
1215                     do j = j to n_pcbs;                     /* loop thru devx table */
1216                          pcbp = addr (fnp_info.pcb_array_ptr -> pcb_array (j));
1217                          if string (pcb.line_number) = string (sub_mbx.line_number)
1218                          then go to match;                  /* check for right slot */
1219                     end;
1220                     call syserr (beeper, "dn355: no slot number match for sub mbx ^o, FNP ^a", i, fnp_info.fnp_tag);
1221                                                             /* bitch */
1222                     call report_fnp_crash;
1223                     go to global_exit;
1224                end;
1225 
1226 match:
1227                devx = pcb.devx;                             /* copy devx to automatic */
1228 
1229           end;
1230           return;
1231 
1232      end /* get_line_number */;
1233 ^L
1234 
1235 /* internal procedure to ship sub mbx off to 355 */
1236 send_mbx:
1237 return_mbx:
1238      proc (a_mbx_no);
1239 
1240 dcl  a_mbx_no fixed bin;
1241 dcl  mbx_no fixed bin;
1242 dcl  pcw_error bit (1);
1243 dcl  timeout_time fixed bin (71);
1244 dcl  1 ima aligned like io_manager_arg;
1245 
1246           mbx_no = a_mbx_no;
1247           go to test_pcw;
1248 
1249 free_mbx:
1250      entry (a_mbx_no);                                      /* this for those which haven't been rewritten */
1251 
1252           mbx_no = a_mbx_no + 4;                            /* use different interrupt level for freeing */
1253 
1254 test_pcw:
1255           if ^fnp_info.io_manager_assigned
1256           then do;
1257                no_response = "1"b;                          /* lie, but effectively */
1258                return;
1259           end;
1260 
1261           no_response = "0"b;
1262           if datanet_mbx.dia_pcw.command ^= "0"b
1263           then do;                                          /* first a quick check to save time */
1264 wait_for_response:
1265                timeout_time = clock () + TWO_SECONDS;
1266                do while ((clock () < timeout_time) & (datanet_mbx.dia_pcw.command ^= "0"b));
1267                                                             /* loop until dia picks up last command */
1268                end;
1269                if datanet_mbx.dia_pcw.error | datanet_mbx.dia_pcw.command ^= "0"b
1270                then do;
1271                     if ^no_response
1272                     then do;
1273                          pcw_error = datanet_mbx.dia_pcw.error;
1274                          no_response = "1"b;
1275                          datanet_mbx.dia_pcw.error = "0"b;
1276                          string (datanet_mbx.dia_pcw) = dn355_util$compute_parity (string (datanet_mbx.dia_pcw));
1277                                                             /* recompute parity */
1278                          ima.chx = fnp_info.io_manager_chx;
1279                          ima.ptp = fnp_info.ptp;
1280                          call io_manager$connect_direct (ima);
1281                                                             /* re-kick FNP */
1282                          call syserr (just_tell,
1283                               "dn355: ^[Error^;Timeout^] sending mailbox interrupt to FNP ^a, will retry.", pcw_error,
1284                               fnp_info.fnp_tag);
1285                          goto wait_for_response;
1286                     end;
1287                end;
1288                else goto send_new_connect;
1289           end;
1290           else do;
1291 send_new_connect:
1292                no_response = "0"b;
1293 
1294                if mbx_no < 8                                /* one of ours */
1295                then do;
1296                     datanet_mbx.mbx_used_flags.used (mbx_no) = "1"b;
1297                     datanet_mbx.num_in_use = datanet_mbx.num_in_use + 1;
1298                     fnp_info.max_mbx_in_use = max (fnp_info.max_mbx_in_use, datanet_mbx.num_in_use);
1299                     fnp_info.cumulative_mbx_in_use = fnp_info.cumulative_mbx_in_use + datanet_mbx.num_in_use;
1300                     fnp_info.mbx_in_use_updated = fnp_info.mbx_in_use_updated + 1;
1301                end;                                         /* set used flag */
1302                string (datanet_mbx.dia_pcw) = initial_pcw;  /* initialize pcw */
1303                datanet_mbx.dia_pcw.mbx_no = bit (fixed (mbx_no, 6), 6);
1304                                                             /* set sub mbx number */
1305 
1306                string (datanet_mbx.dia_pcw) = dn355_util$compute_parity (string (datanet_mbx.dia_pcw));
1307                                                             /* set the parity bit; bit 22 */
1308 
1309                ima.chx = fnp_info.io_manager_chx;
1310                ima.ptp = fnp_info.ptp;
1311                call io_manager$connect_direct (ima);        /* kick 355 */
1312 
1313                return;                                      /* return to caller */
1314           end;
1315      end send_mbx;
1316 ^L
1317 /* entry and internal proc to hangup all lines on an FNP */
1318 
1319 hangup_fnp_lines:
1320      entry (a_fnp_no);
1321 
1322 dcl  a_fnp_no fixed bin;
1323 
1324           ttybp = addr (tty_buf$);
1325           infop = addr (dn355_data$);
1326           call hangup_fnp (a_fnp_no);
1327           return;
1328 
1329 
1330 hangup_fnp:
1331      proc (fnp_no);
1332 
1333 dcl  fnp_no fixed bin;
1334 
1335           fnpp = addr (datanet_info.per_datanet (fnp_no));
1336           n_pcbs = fnp_info.no_of_channels;
1337           do j = 1 to n_pcbs;
1338                pcbp = addr (fnp_info.pcb_array_ptr -> pcb_array (j));
1339                if pcb.dialed
1340                then do;
1341                     call throw_away_output;
1342                     call channel_manager$interrupt ((pcb.devx), CRASH, ""b);
1343                end;
1344           end;
1345 
1346           if fnp_info.count > 0                             /* get rid of any outstanding delay queue entries */
1347           then do;
1348                q_count = fnp_info.count;
1349                q_first = fnp_info.cur_ptr;
1350                do q_count = q_count to 0 by -1 while (q_first ^= 0);
1351                     qptr = ptr (ttybp, q_first);            /* get real pointer to queue entry */
1352                     q_first = qptr -> q_entry.next;         /* save pointer to next one */
1353                     call tty_space_man$free_space (size (q_entry), qptr);
1354                end;
1355 
1356                fnp_info.count, fnp_info.cur_ptr, fnp_info.last_ptr = 0;
1357           end;
1358 
1359      end;
1360 
1361 
1362 
1363 throw_away_output:
1364      proc;
1365 
1366 /* throws away pending write chain on quit and hangup */
1367 
1368           if pcb.write_first ^= 0
1369           then do;
1370                call tty_space_man$free_chain ((pcb.devx), OUTPUT, ptr (ttybp, pcb.write_first));
1371                pcb.write_first, pcb.write_last, pcb.write_cnt = 0;
1372 
1373           end;
1374 
1375           pcb.end_frame = "0"b;
1376 
1377           return;
1378      end /* throw_away_output */;
1379 ^L
1380 /* internal procedure to report that DIA never set PCW to 0 */
1381 
1382 report_fnp_no_response:
1383      proc;
1384 
1385           call syserr (beeper, "dn355: FNP ^a did not respond to mailbox interrupt", fnp_info.fnp_tag);
1386           call report_fnp_crash;                            /* treat it like a crash */
1387           return;
1388 
1389      end /* report_fnp_no_response */;
1390 
1391 
1392 /* internal procedure to tell initializer and clean up when FNP crashes */
1393 
1394 report_fnp_crash:
1395      proc;
1396 
1397           fnp_info.running = "0"b;                          /* it isn't any more */
1398           if fnp_info.dump_patch_in_progress                /* somebody's waiting for this */
1399           then call pxss$notify (FNP_DUMP_PATCH_EVENT);     /* don't let them wait forever */
1400 
1401           if ^fnp_info.bootloading                          /* if we weren't still loading it */
1402           then                                              /* now report hangups for all lines that were dialed to it */
1403                call hangup_fnp (dno);
1404           else fnp_info.bootloading = "0"b;
1405 
1406           auto_fnp_msg.state = FNP_DOWN;                    /* tell the responsible process */
1407           auto_fnp_msg.fnp_no = dno;
1408           auto_fnp_msg.flags = "0"b;
1409           unspec (fnp_event_message) = unspec (auto_fnp_msg);
1410           call pxss$ring_0_wakeup (fnp_info.boot_process_id, fnp_info.boot_ev_chan, fnp_event_message, 0);
1411 
1412           return;
1413 
1414      end report_fnp_crash;
1415 check_lock:
1416      proc;
1417 
1418 /* the cleanup procedure -- makes sure we don't crawl out with lock set */
1419 
1420           if queue_locked
1421           then call syserr (crash_system, "dn355: attempted crawlout with FNP queue locked");
1422 
1423           else if masked
1424           then call pmut$unwire_unmask (wire_arg, wire_ptr);/* it's probably too late, but just in case */
1425 
1426           return;
1427      end check_lock;
1428 %page;
1429 /* Main program declarations */
1430 
1431 dcl  (dcwlptr, bufp, qptr) ptr,                             /* random pointers used */
1432      timw fixed bin (24),                                   /* local slot for mailbox timw */
1433      (level, dno, i, ix, q_first, q_count, chars_left, numchars, k, j, chain_len) fixed bin,
1434                                                             /* random halfwords used */
1435      devx fixed bin,                                        /* index of current channel */
1436      operation fixed bin (8),                               /* local slot for delay queue operation */
1437      lano bit (3) unal;                                     /* local slot for line number */
1438 dcl  fnp_name char (1) aligned;                             /* for syserr calls */
1439 
1440 dcl  no_response bit (1) aligned;                           /* set by send_mbx to indicate that DIA didn't respond */
1441 
1442 dcl  interrupt_entry bit (1);                               /* whether entered through dn355$interrupt */
1443 
1444 dcl  input_count fixed bin;                                 /* count sent with accept_dir_input */
1445 
1446 dcl  chan_lctep ptr;                                        /* pointer to subchannel's LCTE */
1447 dcl  chain_head_ptr ptr;                                    /* pointer to output chain to be freed */
1448 dcl  output_limit fixed bin;                                /* maximum number of output chars to be sent at once */
1449 dcl  output_chars fixed bin;                                /* number of output chars sent so far */
1450 
1451 dcl  bits_per_char fixed bin;
1452 dcl  max_buf_chars fixed bin;                               /* number of characters to go in largest buffer at this speed */
1453 
1454 dcl  wire_arg fixed bin (71);
1455 dcl  wire_ptr ptr;
1456 dcl  masked bit (1);
1457 dcl  queue_locked bit (1);
1458 dcl  continue bit (1);                                      /* for premature termination of loops */
1459 
1460 dcl  offset fixed bin;                                      /* offset of error message in dn355_messages */
1461 dcl  syserr_severity fixed bin (35);
1462 
1463 /* Codes used for syserr are declared here because syserr_constants.incl.pl1 cannot
1464    be used, owing to a naming conflict with mcs_interrupt_info.incl.pl1.
1465 */
1466 
1467 dcl  (
1468      dataoff init (1),                                      /* offset in buffer of data */
1469      max_chain_len init (16),                               /* maximum dcw chain length */
1470      just_tell init (0),                                    /* syserr message, no alarm */
1471      beeper init (3),                                       /* syserr ring beeper */
1472      log init (4),
1473      crash_system init (1)
1474      ) fixed bin int static options (constant);             /* argument to syserr */
1475 
1476 dcl  FNP_DOWN fixed bin int static options (constant) init (2);
1477 
1478 dcl  initial_pcw bit (36) int static init ("000000000000000000000000000000111001"b);
1479                                                             /* initial dia pcw */
1480 dcl  TANDD_LINE_NUMBER bit (10) int static options (constant) init ((10)"1"b);
1481                                                             /* i.e., 1777 octal */
1482 dcl  TWO_SECONDS fixed bin (71) int static options (constant) init (2000000);
1483                                                             /* used to wait for DIA to clear PCW */
1484 
1485 dcl  timwb (0:11) bit (1) based (addr (timw)),              /* timw as a bit array */
1486      used_string bit (8) based (addr (datanet_mbx.mbx_used_flags.used (0)));
1487                                                             /* mailbox used flags as a bit string */
1488 
1489 dcl  (addr, binary, substr, stac, stacq, string, ptr, rel, index, fixed, divide, bin, max, min, null, length, bit, unspec,
1490      hbound, size, verify, clock) builtin;                  /* builtin functions used */
1491 
1492 dcl  unal_number fixed bin (17) unal based,                 /* handy way of referencing an unaligned number */
1493      chars char (numchars) based;                           /* handy way of moving character strings */
1494 
1495 dcl  input_chars char (chars_left) based;                   /* for scanning entire input */
1496 
1497 dcl  smbx_cmd_data_long bit (216) unaligned based (addr (sub_mbx.command_data));
1498 
1499 dcl  tc_data$system_shutdown ext fixed bin;                 /* external variables used */
1500 dcl  tc_data$fnp_buffer_threshold ext static fixed bin;
1501 dcl  pds$processid bit (36) aligned ext static;
1502 
1503 dcl  ff_cr_lf char (3) int static options (constant) init ("^L^M
1504 ");
1505 
1506 dcl  form_feed init ("^L") char (1) int static options (constant);
1507 
1508 dcl  syserr entry options (variable),                       /* external entries called */
1509      ldac entry (ptr) returns (fixed bin (24)),
1510      dn355_util$compute_parity entry (bit (36)) returns (bit (36)),
1511      (
1512      dn355_boot_interrupt,
1513      dn355_boot_interrupt$system_fault
1514      ) entry (fixed bin),
1515      pxss$ring_0_wakeup entry (bit (36) aligned, fixed bin (71), fixed bin (71), fixed bin (35)),
1516      pxss$unique_ring_0_wakeup entry (bit (36) aligned, fixed bin (71), fixed bin (71), fixed bin (35)),
1517      pxss$notify entry (fixed bin);
1518 
1519 dcl  pmut$wire_and_mask entry (fixed bin (71), ptr);
1520 dcl  pmut$unwire_unmask entry (fixed bin (71), ptr);
1521 dcl  1 auto_net_event_message aligned like net_event_message;
1522 dcl  1 auto_fnp_msg aligned like fnp_msg;
1523 dcl  fnp_event_message fixed bin (71);
1524 
1525 dcl  1 dcw_list (max_chain_len) aligned based (dcwlptr),    /* dcw list for output */
1526        2 dcw_ptr bit (18) unal,                             /* pointer to buffer */
1527        2 pad bit (9) unal,                                  /* unused */
1528        2 dcw_tally bit (9) unal;                            /* tally */
1529 
1530 dcl  1 dcw_list_array (0:7) aligned based,
1531        2 dcw_list_template (max_chain_len) like dcw_list;
1532 
1533 dcl  1 q_entry aligned like fnp_queue_entry based (qptr);
1534 
1535 dcl  new_qp ptr;                                            /* temporary to newly-allocated block */
1536 dcl  new_qrel fixed bin;
1537 
1538 dcl  1 dn355_word unal based (addr (datanet_mbx.fault_word)),
1539                                                             /* format of 355 crash word */
1540        2 modnum bit (4),                                    /* module number (1 - 8) */
1541        2 opcode fixed bin (4),
1542        2 crash_code fixed bin (8);                          /* used to index list of messages */
1543 
1544 dcl  fault_type fixed bin;                                  /* 355 fault code */
1545 dcl  fault_name char (16);                                  /* 355 fault name */
1546 dcl  module_num fixed bin;                                  /* 355 module number */
1547 
1548 dcl  iom_channel_fault fixed bin int static init (9);
1549 dcl  illegal_opcode fixed bin int static init (3);
1550 dcl  die_code fixed bin int static init (9);
1551 
1552 dcl  1 error_msg aligned based (addr (sub_mbx.command_data (1))),
1553                                                             /* error message data */
1554        2 data (4) bit (18) unal;
1555 
1556 dcl  full_words (3) fixed bin;
1557 
1558 dcl  reason_msg char (64);
1559 
1560 dcl  cleanup condition;
1561 %page;
1562 %include baud_rates;
1563 %page;
1564 %include channel_manager_dcls;
1565 %page;
1566 %include dn355_data;
1567 %page;
1568 %include dn355_mailbox;
1569 %page;
1570 %include dn355_messages;
1571 %page;
1572 %include fnp_mpx_msg_;
1573 %page;
1574 %include fnp_queue_entry;
1575 %page;
1576 %include io_manager_dcls;
1577 %page;
1578 %include lct;
1579 %page;
1580 %include line_types;
1581 %page;
1582 %include mailbox_ops;
1583 %page;
1584 %include mcs_interrupt_info;
1585 %page;
1586 %include net_event_message;
1587 %page;
1588 %include pcb;
1589 %page;
1590 %include tty_buf;
1591 %page;
1592 %include tty_buffer_block;
1593 %page;
1594 %include tty_space_man_dcls;
1595 %page;
1596 /* BEGIN MESSAGE DOCUMENTATION
1597 
1598    Message:
1599    dn355: FNP X invalid interrupt level N
1600 
1601    S:  $beep
1602 
1603    T:  $run
1604 
1605    M:  An FNP interrupt has been received from frontend X with an invalid
1606    interrupt level of octal value N and will be ignored.  If this message is
1607    displayed when a DN6670 is being powered up, this message can be ignored.
1608    If this message occurs under any other circumstances, there might be
1609    something wrong with the system's interface with the FNP and should be
1610    investigated by FE representatives.
1611 
1612    A:  $inform
1613 
1614 
1615    Message:
1616    dn355: emergency interrupt from FNP X: FAULT
1617    .br
1618    FNP instruction counter = IC
1619    .br
1620    channel CHN, fault status = FS
1621    .br
1622    FNP_MODULE: REASON_FOR_CRASH
1623 
1624    S:  $beep
1625 
1626    T:  $run
1627 
1628    M:  An emergency interrupt has been received from FNP X indicating
1629    it has crashed.  All lines dialed to FNP X will be hung up.  The
1630    crash was nominally caused by a fault of type FAULT.  Lines
1631    following the first line of the message appear only in certain cases
1632    and provide additional information about the nature of the crash.
1633 
1634    A:  The system will automatically attempt to reboot the crashed FNP.
1635    Subsequent messages will indicate the success or failure of this attempt.
1636    No action is required now, but action may be required if the
1637    automatic reboot fails.
1638 
1639 
1640    Message:
1641    dn355: no slot number match for sub mbx N, FNP X
1642 
1643    S:  $beeper
1644 
1645    T:  $run
1646 
1647    M:  An error has occurred processing submailbox N for FNP X.
1648    The submailbox indicates a line number for which no match could
1649    be found.
1650 
1651    A:  $inform
1652 
1653 
1654    Message:
1655    dn355: Message from FNP X: MESSAGE
1656 
1657    S:  $info
1658 
1659    T:  $run
1660 
1661    M:  An error has been detected by FNP X as explained by MESSAGE.
1662 
1663    A:  No action is required by the operator to deal with the error mentioned
1664    in the message.  Action may be required by appropriate personnel to correct
1665    the problem that caused the error and undo what the FNP may have done to
1666    continue operation.  This may require shutting down the FNP for repairs by
1667    Field Engineering and reboot of the FNP to restore full operation.
1668 
1669 
1670    Message:
1671    dn355: FNP masked channel NAME for excessive interrupts
1672 
1673    S:  $info
1674 
1675    T:  $run
1676 
1677    M:  The FNP has masked the channel whose name is NAME because it was
1678    generating interrupts faster than they could be handled.
1679 
1680    A: The interruptions can be caused by any number of problems.  This
1681    can be caused by the dataset leads changing too fast for the FNP
1682    software to handle properly; disconnecting or connecting FNP cables,
1683    powering off or on a hardwired terminal, a bad modem, etc.  It is
1684    also possible that the FNP channel hardware is defective.  Future
1685    attempts to use this channel may possibly crash the FNP.  CS
1686    representatives may need to be called to investigate.  An "attach"
1687    command will be necessary to put the channel back in service.
1688 
1689 
1690    Message:
1691    dn355: unrecognized op code OPCODE with rcd from FNP X for devx N
1692 
1693    S:  $beeper
1694 
1695    T:  $run
1696 
1697    M:  An invalid op code, OPCODE, has been received from FNP X for device
1698    index N in a mailbox containing an rcd (read control data) command.
1699 
1700    A:  $inform
1701 
1702 
1703    Message:
1704    dn355: unrecognized io command C from FNP X for line N
1705 
1706    S:  $beeper
1707 
1708    T:  $run
1709 
1710    M:  An invalid io command was received from FNP X for line N. C is the
1711    octal representation of the command.
1712 
1713    A:  $inform
1714 
1715 
1716    Message:
1717    dn355: output buffer at N has zero tally
1718 
1719    S:  $crash
1720 
1721    T:  $run
1722 
1723    M:  An output buffer with a zero tally has been found at offset N
1724    in the segment tty_buf.
1725 
1726    A:  $inform
1727 
1728 
1729    Message:
1730    dn355: unable to allocate block for delay queue
1731 
1732    S:  $crash
1733 
1734    T:  $run
1735 
1736    M: There was insufficient space left in tty_buf to allocate a block
1737    in which to build a delay queue.
1738 
1739    A:  $inform
1740 
1741 
1742    Message:
1743    dn355: FNP X did not respond to mailbox interrupt
1744 
1745    S:  $beep
1746 
1747    T:  $run
1748 
1749    M:  An attempt to interrupt FNP X was unsuccessful. The FNP is assumed
1750    to be down.
1751 
1752    A:  The system will automatically attempt to reboot the crashed FNP.
1753    Subsequent messages will indicate the success or failure of this attempt.
1754    No action is required now, but action may be required if the
1755    automatic reboot fails.
1756 
1757 
1758    Message:
1759    dn355: inconsistent queue lock
1760 
1761    S:  $crash
1762 
1763    T:  $run
1764 
1765    M:  A process attempted to unlock the interrupt queue lock without having it
1766    locked.
1767 
1768    A:  $inform
1769 
1770 
1771    Message:
1772    dn355: LCTE lock ^= processid
1773 
1774    S:     $crash
1775 
1776    T:     $run
1777 
1778    M:  The FNP channel lock did not contain the processid of the process
1779    attempting to unlock it.
1780 
1781 
1782    Message:
1783    dn355: attempted crawlout with FNP queue locked
1784 
1785    S:     $crash
1786 
1787    T:     $run
1788 
1789    M:     An attempt was made to crawl out while an FNP queue lock (a processor
1790    lock) was locked.
1791 
1792    A:     $inform
1793 
1794 
1795    Message:
1796    dn355: line number of 0 with non-global opcode in submbx N, FNP X
1797 
1798    S:  $beeper
1799 
1800    T:  $run
1801 
1802    M:  Mailbox N from FNP X contained a non-global opcode which requires a
1803    non-zero line number.
1804 
1805 
1806    Message:
1807    dn355: FNP X level L status S STATE
1808 
1809    S:   $info
1810 
1811    T:   $run
1812 
1813    M:   An interrupt at a level other than 3 (or possibly at level 3 if the
1814    FNP is not running) was received from FNP X. S is an octal representation
1815    of the status accompanying the interrupt. STATE indicates the current state
1816    of the FNP: running, bootloading, or in T&D. This message only appears if
1817    tracing is enabled for the specified FNP.
1818 
1819    A:   None required.
1820 
1821 
1822    Message:
1823    dn355: Error sending mailbox interrupt to FNP X, will retry.
1824 
1825    S:  $info
1826 
1827    T:  $run
1828 
1829    M:  An error has been detected in the transmission of a mailbox interrupt
1830    to the FNP.  The transmission will be retried once.
1831 
1832    A:  None required.
1833 
1834 
1835    Message:
1836    dn355: Timeout sending mailbox interrupt to FNP X, will retry.
1837 
1838    S:  $info
1839 
1840    T:  $run
1841 
1842    M:  The FNP did not respond within 2 seconds to the previously
1843    sent mailbox interrupt.  The transmission will be retried once.
1844 
1845    A:  None required.
1846 
1847 
1848    END MESSAGE DOCUMENTATION */
1849 
1850 
1851      end dn355;