?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Deadstart : System Messages Processor' ??
MODULE dsm$process_system_messages;

{ PURPOSE:
{   This module contains procedures that manage the System Message buffer.  These procedures add messages
{   to the buffer, remove messages from the buffer and enlarge the buffer.  The procedures to process the
{   OS action code in the errors that DFT logs in the buffer control words in the DFT block are also
{   included in this module.  Some of the information used in this module is obtained from the DFT/OS
{   Interface Specification document, ARH6853.

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc cml$system_informative_message
*copyc cyd$cybil_structure_definitions
*copyc dpt$top_line_message
*copyc dst$dft_analysis_code_constants
*copyc dst$mf_element_table_entry
*copyc dst$os_action_code_constants
*copyc dst$rb_logging_request
*copyc dst$signal_contents
*copyc dst$ssr_data_types
*copyc dst$system_message_types
*copyc mmt$page_frame_index
*copyc osc$maximum_processor_number
*copyc osc$monitor_stack_mult
*copyc ost$free_running_clock
*copyc ost$hardware_subranges
?? POP ??
*copyc dpp$display_error
*copyc dsp$convert_r_pointer_to_seq_p
*copyc dsp$mtr_handle_bit_57
*copyc dsp$mtr_handle_pp_hang
*copyc dsp$mtr_save_mainframe_error
*copyc i#current_sequence_position
*copyc i#mtr_disable_traps
*copyc i#move
*copyc i#mtr_restore_traps
*copyc i#test_set_bit
*copyc mmp$mark_page_flawed
*copyc mtp$cst_p
*copyc mtp$deconfigure_divide_unit
*copyc mtp$get_date_time_at_timestamp
*copyc mtp$record_critical_hdw_status
*copyc mtp$record_noncrit_hdw_status
*copyc tmp$clear_lock
*copyc tmp$new_clear_lock
*copyc tmp$send_signal
*copyc tmp$new_set_lock
*copyc tmp$set_system_flag
?? EJECT ??
*copyc dsv$mainframe_type
*copyc mtv$170_due_info
*copyc mtv$cst0
*copyc mtv$dft_block_p
*copyc mtv$monitor_exchange_package
*copyc mtv$monitor_xp_slot_pointers
*copyc mtv$scb
*copyc tmv$system_job_monitor_gtid
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??
  CONST
    c$number_of_hardware_messages = 15;

  TYPE
    t$buffer = RECORD
      data_from_mrb: boolean,
      data_p: ^cell,
    RECEND,

    t$buffer_time = RECORD
      CASE 0 .. 2 OF
      = 0 =
        rfu_0_a: 0 .. 0ffffffff(16),
        hours: 0 .. 0ff(16),
        minutes: 0 .. 0ff(16),
        seconds: 0 .. 0ff(16),
        rfu_0_b: 0 .. 0ff(16),
      = 1 =
        rfu_1: 0 .. 0ffffffff(16),
        time: 0 .. 0ffffffff(16),
      = 2 =
        date_and_time: dst$dftb_date_and_time,
      CASEND,
    RECEND,

    t$console_message_record = RECORD
      hours: string (2),
      period_1: string (1),
      minutes: string (2),
      period_2: string (1),
      seconds: string (2),
      blank_1: string (1),
      error_string: string (4),
      code: string (12),
      blank_2: string (1),
      english_string: string (46),
    RECEND,

    t$fault_symptom_code = RECORD
      CASE 0 .. 2 OF
      = 0 =
        rfu_0: 0 .. 0ffffffff(16),
        code_string: string (12),
      = 1 =
        rfu_1: 0 .. 0ffffffff(16),
        code_1: 0 .. 0ffffffffffff(16),
        code_2: 0 .. 0ffffffffffff(16),
      = 2 =
        words: dst$dftb_fault_symptom_words,
      CASEND,
    RECEND,

    t$hardware_messages_record = RECORD
      dft_analysis_code: dst$dftb_dft_analysis_code,
      insert_number_location: 0 .. 40,
      message: string (40),
    RECEND,

    t$hardware_message_type = (c$hmt_no_message, c$hmt_180_message, c$hmt_170_message,
          c$hmt_version_3_180_message, c$hmt_version_3_170_message),

    t$partial_nrb_record = RECORD
      control_word: dst$dftb_buffer_control_word,
      information_word: dst$dftb_nrb_information_word,
      buffer_time: t$buffer_time,
      fault_symptom_code: t$fault_symptom_code,
    RECEND,

    t$partial_ssb_record = RECORD
      information_word: dst$dftb_ssb_information_word,
      buffer_time: t$buffer_time,
      fault_symptom_code: t$fault_symptom_code,
    RECEND;
?? EJECT ??
  VAR
    dsv$dftb_data: [XDCL, #GATE] dst$dftb_data_structure_info := [0, 0, 0, 0, 0, 0, 0, 0, 0],
    dsv$dftb_nve_req_buffer_p: [XDCL] ^dst$dftb_nve_req_buffer := NIL,
    dsv$dfts_control_word_p: [XDCL, #GATE] ^dst$dftb_control_word := NIL,
    dsv$mf_element_table_p: [XDCL, #GATE] ^ARRAY [1 .. *] OF dst$mf_element_table_entry := NIL,
    dsv$record_errors: [XDCL, #GATE] boolean := FALSE,
    dsv$sys_msg_buffer_desc_p: [XDCL, #GATE] ^dst$sys_msg_buffer_desc,
    dsv$sys_msg_buffer_initialized: [XDCL, #GATE] boolean := FALSE,
    dsv$sys_msg_buffer_ptrs: [XDCL, #GATE] dst$sys_msg_buffer_ptrs,
    dsv$sys_msg_buffer_size: [XDCL, #GATE] integer := dsc$sys_msg_buffer_size,
    dsv$turn_dft_logging_off: [XDCL, #GATE] boolean := FALSE,

    v$buffer: t$buffer := [TRUE, NIL],
    v$dft_hardware_messages: [READ] ARRAY [1 .. c$number_of_hardware_messages] OF
          t$hardware_messages_record :=
          [[dsc$dftb_dac_iou_004, 16, 'UNCORRECTED IOU00 ERROR'],
           [dsc$dftb_dac_iou_009, 16, 'UNCORRECTED IOU00 ERROR'],
           [dsc$dftb_dac_iou_006, 10, 'FATAL IOU00 ERROR'],
           [dsc$dftb_dac_iou_008, 10, 'FATAL IOU00 ERROR'],
           [dsc$dftb_dac_mem_105, 00, 'FATAL CM ERROR (MULTIPLE ODD BIT MEMORY)'],
           [dsc$dftb_dac_mem_106, 00, 'FATAL CM ERROR (PARTIAL WRITE PARITY)'],
           [dsc$dftb_dac_cpu_21E, 20, 'FATAL CM ERROR (CPU00 - PARTIAL WRITE)'],
           [dsc$dftb_dac_non_701, 00, 'ENVIRONMENT WARNING'],
           [dsc$dftb_dac_non_702, 00, 'LONG POWER WARNING'],
           [dsc$dftb_dac_non_703, 00, 'SHORT POWER WARNING'],
           [dsc$dftb_dac_non_704, 00, 'ENVIRONMENT WARNING NORMAL'],
           [dsc$dftb_dac_non_705, 00, 'LONG POWER WARNING NORMAL'],
           [dsc$dftb_dac_non_706, 00, 'SHORT POWER WARNING NORMAL'],
           [dsc$dftb_dac_non_709, 00, 'LONG POWER WARNING'],
           [dsc$dftb_dac_non_70A, 00, 'LONG POWER WARNING NORMAL']],

    v$dftb_buffer_entries_checked: 0 .. 10 := 0,
    v$dftb_data_retrieved: boolean := FALSE,

    v$dftb_mdb_p: ^ARRAY [0 .. *] OF dst$r_pointer := NIL,
    v$dftb_mrb_cw_offset: ost$real_memory_address := 0,
    v$dftb_mrb_offset: ost$real_memory_address := 0,
    v$dftb_nrb_offset: ost$real_memory_address := 0,
    v$dftb_ssb_offset: ost$real_memory_address := 0,

    v$sys_msg_buffer_is_full: boolean := FALSE,
    v$sys_msg_buffer_lock: tmt$new_ptl_lock := [FALSE, 0];
?? OLDTITLE ??
?? NEWTITLE := 'access_buffer_entry', EJECT ??

{ PURPOSE:
{   This procedure accesses a buffer entry from the DFT buffer.

  PROCEDURE access_buffer_entry
    (VAR rb: dst$rb_logging_request);

    VAR
      adjusted_pva_p: ^cell,
      buffer_cw_p: ^dst$dftb_buffer_control_word,
      cw_index: dst$dftb_element_size,
      interlock_previously_set: boolean,
      interlocked_entry_found: boolean,
      lowest_sequence_number: dst$dftb_sequence_number,
      mdb_buffer_data_p: ^SEQ ( * ),
      mdb_iw_p: ^dst$dftb_mdb_information_word,
      mdb_seq_p: ^SEQ ( * ),
      nrb_p: ^t$partial_nrb_record,
      rb_buffer_data_p: ^SEQ ( * ),
      skip_buffer_data_p: ^SEQ ( * ),
      ssb_p: ^t$partial_ssb_record;

    IF dsv$turn_dft_logging_off THEN
      RETURN;
    IFEND;

    IF NOT v$dftb_data_retrieved THEN
      setup_variables_to_dft_block;
    IFEND;

    { Clear the logging bit on the buffer entry that was just logged.

   /clear_logging_bit/
    BEGIN
      IF rb.dftb_data_structures = $dst$rb_dft_data_structures[ ] THEN
        EXIT /clear_logging_bit/;
      IFEND;

      IF dsc$dds_mrb IN rb.dftb_data_structures THEN
        buffer_cw_p := #ADDRESS (1, #SEGMENT (mtv$dft_block_p),
              v$dftb_mrb_cw_offset + ((rb.dftb_cw_index - 1) * #SIZE (dst$dftb_buffer_control_word)));
        ssb_p := #ADDRESS (1, #SEGMENT (mtv$dft_block_p),
              v$dftb_ssb_offset + ((rb.dftb_cw_index - 1) * (dsv$dftb_data.ssb_length * 8)));
      ELSEIF dsc$dds_nrb IN rb.dftb_data_structures THEN
        nrb_p := #ADDRESS (1, #SEGMENT (mtv$dft_block_p),
              v$dftb_nrb_offset + ((rb.dftb_cw_index - 1) * (dsv$dftb_data.nrb_length * 8)));
        buffer_cw_p := ^nrb_p^.control_word;
      ELSE
        EXIT /clear_logging_bit/;
      IFEND;

      IF NOT buffer_cw_p^.flags.c180_valid_data THEN
        i#test_set_bit (^buffer_cw_p^, dsc$dftb_interlock_bit, interlock_previously_set);
        IF interlock_previously_set THEN
          IF rb.dftb_clear_entries_checked < 10 THEN
            rb.dftb_clear_entries_checked := rb.dftb_clear_entries_checked + 1;
            rb.response := dsc$rlr_dft_entry_interlocked;
            RETURN;
          IFEND;
          rb.dftb_clear_entries_checked := 0;
          EXIT /clear_logging_bit/;
        IFEND;

        { Clear the MDB control word offset.  This allows DFT to reuse the buffer.

        IF (dsc$dds_mdb IN rb.dftb_data_structures) AND (buffer_cw_p^.flags.valid_mdb_data) THEN
          IF ((ssb_p^.information_word.mdb_ordinal >= 0) AND
                (ssb_p^.information_word.mdb_ordinal < dsv$dftb_data.number_of_mdbs)) AND
                (ssb_p^.information_word.unlogged = 0) THEN
            dsp$convert_r_pointer_to_seq_p (v$dftb_mdb_p^ [ssb_p^.information_word.mdb_ordinal],
                  mtv$dft_block_p, mdb_seq_p);
            NEXT mdb_iw_p IN mdb_seq_p;
            mdb_iw_p^.control_word_offset := 0;
          IFEND;
        IFEND;

        buffer_cw_p^.flags.logging_action := FALSE;
        buffer_cw_p^.flags.interlock := FALSE;
      IFEND;
    END /clear_logging_bit/;

    rb.response := dsc$rlr_dft_no_entry_to_log;
    rb.dftb_cw_index := 0;
    rb.dftb_data_structures := $dst$rb_dft_data_structures[ ];
    interlocked_entry_found := FALSE;

   /check_mrb_entries/
    BEGIN
      IF v$dftb_mrb_cw_offset <> 0 THEN

      { Search the MRB for an entry that has been processed but not logged and not interlocked.

       /search_mrb_entries/
        WHILE TRUE DO
          rb.dftb_cw_index := 0;
          lowest_sequence_number := UPPERVALUE (dst$dftb_sequence_number);
          FOR cw_index := 1 TO dsv$dftb_data.number_of_mrbs DO
            buffer_cw_p := #ADDRESS (1, #SEGMENT (mtv$dft_block_p),
                  v$dftb_mrb_cw_offset + ((cw_index - 1) * #SIZE (dst$dftb_buffer_control_word)));
            IF (buffer_cw_p^.flags.logging_action) AND (NOT buffer_cw_p^.flags.c180_valid_data) THEN
              IF NOT buffer_cw_p^.flags.interlock THEN
                IF buffer_cw_p^.sequence_number <= lowest_sequence_number THEN
                  rb.dftb_cw_index := cw_index;
                  lowest_sequence_number := buffer_cw_p^.sequence_number;
                IFEND;
              ELSE
                interlocked_entry_found := TRUE;
              IFEND;
            IFEND;
          FOREND;
          IF rb.dftb_cw_index <= 0 THEN
            EXIT /check_mrb_entries/;
          IFEND;

          buffer_cw_p := #ADDRESS (1, #SEGMENT (mtv$dft_block_p),
                v$dftb_mrb_cw_offset + ((rb.dftb_cw_index - 1) * #SIZE (dst$dftb_buffer_control_word)));
          i#test_set_bit (^buffer_cw_p^, dsc$dftb_interlock_bit, interlock_previously_set);
          IF NOT interlock_previously_set THEN
            EXIT /search_mrb_entries/;
          IFEND;
        WHILEND /search_mrb_entries/;

        { Move the MRB data to log into the return sequence.

        rb.dftb_data_structures := $dst$rb_dft_data_structures [dsc$dds_mrb];
        rb.dftb_control_word := buffer_cw_p^;
        adjusted_pva_p := #ADDRESS (1, #SEGMENT (mtv$dft_block_p),
              v$dftb_mrb_offset + (buffer_cw_p^.offset * 8));
        i#move (adjusted_pva_p, rb.dftb_seq_p, (dsv$dftb_data.mrb_length * 8));

        IF v$dftb_ssb_offset <> 0 THEN

          { Move the SSB data to the return sequence.

          RESET rb.dftb_seq_p;
          NEXT skip_buffer_data_p: [[REP dsv$dftb_data.mrb_length OF integer]] IN rb.dftb_seq_p;
          NEXT rb_buffer_data_p: [[REP dsv$dftb_data.ssb_length OF integer]] IN rb.dftb_seq_p;
          ssb_p := #ADDRESS (1, #SEGMENT (mtv$dft_block_p),
                v$dftb_ssb_offset + ((rb.dftb_cw_index - 1) * (dsv$dftb_data.ssb_length * 8)));
          i#move (ssb_p, rb_buffer_data_p, (dsv$dftb_data.ssb_length * 8));
          rb.dftb_data_structures := rb.dftb_data_structures + $dst$rb_dft_data_structures [dsc$dds_ssb];

          { Move any MDB data to the return sequence.

          IF (v$dftb_mdb_p <> NIL) AND (buffer_cw_p^.flags.valid_mdb_data) THEN
            IF ((ssb_p^.information_word.mdb_ordinal >= 0) AND
                  (ssb_p^.information_word.mdb_ordinal < dsv$dftb_data.number_of_mdbs)) AND
                  (ssb_p^.information_word.unlogged = 0) THEN
              dsp$convert_r_pointer_to_seq_p (v$dftb_mdb_p^ [ssb_p^.information_word.mdb_ordinal],
                    mtv$dft_block_p, mdb_seq_p);
              NEXT mdb_buffer_data_p: [[REP dsv$dftb_data.mdb_length OF integer]] IN mdb_seq_p;
              NEXT rb_buffer_data_p: [[REP dsv$dftb_data.mdb_length OF integer]] IN rb.dftb_seq_p;
              rb_buffer_data_p^ := mdb_buffer_data_p^;
              rb.dftb_data_structures := rb.dftb_data_structures + $dst$rb_dft_data_structures [dsc$dds_mdb];
            IFEND;
          IFEND;
        IFEND;

        RESET rb.dftb_seq_p;
        buffer_cw_p^.flags.interlock := FALSE;
        rb.response := dsc$rlr_dft_entry_to_log;
        RETURN;
      IFEND;
    END /check_mrb_entries/;

   /check_nrb_entries/
    BEGIN
      IF v$dftb_nrb_offset <> 0 THEN

        { Search the NRB for an entry that has been processed but not logged and not interlocked.

       /search_nrb_entries/
        WHILE TRUE DO
          rb.dftb_cw_index := 0;
          lowest_sequence_number := UPPERVALUE (dst$dftb_sequence_number);
          FOR cw_index := 1 TO dsv$dftb_data.number_of_nrbs DO
            nrb_p := #ADDRESS (1, #SEGMENT (mtv$dft_block_p),
                  v$dftb_nrb_offset + ((cw_index - 1) * (dsv$dftb_data.nrb_length * 8)));
            buffer_cw_p := ^nrb_p^.control_word;
            IF (buffer_cw_p^.flags.logging_action) AND (NOT buffer_cw_p^.flags.c180_valid_data) THEN
              IF NOT buffer_cw_p^.flags.interlock THEN
                IF buffer_cw_p^.sequence_number <= lowest_sequence_number THEN
                  rb.dftb_cw_index := cw_index;
                  lowest_sequence_number := buffer_cw_p^.sequence_number;
                IFEND;
              ELSE
                interlocked_entry_found := TRUE;
              IFEND;
            IFEND;
          FOREND;
          IF rb.dftb_cw_index <= 0 THEN
            EXIT /check_nrb_entries/;
          IFEND;

          nrb_p := #ADDRESS (1, #SEGMENT (mtv$dft_block_p),
                v$dftb_nrb_offset + ((rb.dftb_cw_index - 1) * (dsv$dftb_data.nrb_length * 8)));
          buffer_cw_p := ^nrb_p^.control_word;
          i#test_set_bit (^buffer_cw_p^, dsc$dftb_interlock_bit, interlock_previously_set);
          IF NOT interlock_previously_set THEN
            EXIT /search_nrb_entries/;
          IFEND;
        WHILEND /search_nrb_entries/;

        { Move the NRB data to log into the return sequence.

        rb.dftb_data_structures := $dst$rb_dft_data_structures [dsc$dds_nrb];
        rb.dftb_control_word := buffer_cw_p^;
        i#move (nrb_p, rb.dftb_seq_p, (dsv$dftb_data.nrb_length * 8));
        RESET rb.dftb_seq_p;
        buffer_cw_p^.flags.interlock := FALSE;
        rb.response := dsc$rlr_dft_entry_to_log;
        RETURN;
      IFEND;
    END /check_nrb_entries/;

    IF interlocked_entry_found AND (rb.dftb_cw_index <= 0) AND (rb.dftb_log_entries_checked < 10) THEN
      rb.dftb_log_entries_checked := rb.dftb_log_entries_checked + 1;
      rb.response := dsc$rlr_dft_entry_interlocked;
    ELSE
      rb.dftb_log_entries_checked := 0;
    IFEND;

  PROCEND access_buffer_entry;
?? OLDTITLE ??
?? NEWTITLE := 'build_fault_symptom_code', EJECT ??

{ PURPOSE:
{   This procedure builds a fault symptom code if one does not exist in the DFT buffer.
{   The fault symptom code is defined to be the following:
{               Demmxxxyyyyy
{     where,
{            D = Character indicating fault symptom code was generated by DFT
{            e = Element specifier
{                     A = DFT internal error
{                     C, D, E, F = CPU 0, 1, 2, 3 respectively
{                     I, J, K, L = IOU 0, 1, 2, 3 respectively
{                     M = Memory
{                     P = Page Maps
{                     R, S, T, U = CPU 4, 5, 6, 7 respectively
{            mm = Model number of element that reported the error
{            xxx = DFT analysis code
{            yyyyy = blanks

  PROCEDURE build_fault_symptom_code
    (    dft_analysis_code: dst$dftb_dft_analysis_code;
         element_entry: dst$mf_element_table_entry;
     VAR fault_symptom_code: string (12));

    fault_symptom_code := ' ';

    fault_symptom_code (7) := CHR ((dft_analysis_code MOD 16) + ORD ('0'));
    fault_symptom_code (6) := CHR (((dft_analysis_code DIV 16) MOD 16) + ORD ('0'));
    fault_symptom_code (5) := CHR (((dft_analysis_code DIV (16*16)) MOD 16) + ORD ('0'));

    fault_symptom_code (3, 2) := element_entry.model_number_string.value;

    fault_symptom_code (2) := 'A';
    CASE element_entry.element_id.dft_entry_id OF
    = dsc$dftb_eid_cpu0_element =
      fault_symptom_code (2) := 'C';
    = dsc$dftb_eid_cpu1_element =
      fault_symptom_code (2) := 'D';
    = dsc$dftb_eid_cpu2_element =
      fault_symptom_code (2) := 'E';
    = dsc$dftb_eid_cpu3_element =
      fault_symptom_code (2) := 'F';
    = dsc$dftb_eid_cpu4_element =
      fault_symptom_code (2) := 'R';
    = dsc$dftb_eid_cpu5_element =
      fault_symptom_code (2) := 'S';
    = dsc$dftb_eid_cpu6_element =
      fault_symptom_code (2) := 'T';
    = dsc$dftb_eid_cpu7_element =
      fault_symptom_code (2) := 'U';
    = dsc$dftb_eid_memory_element =
      fault_symptom_code (2) := 'M';
    = dsc$dftb_eid_iou0_element =
      fault_symptom_code (2) := 'I';
    = dsc$dftb_eid_iou1_element =
      fault_symptom_code (2) := 'J';
    = dsc$dftb_eid_iou2_element =
      fault_symptom_code (2) := 'K';
    = dsc$dftb_eid_iou3_element =
      fault_symptom_code (2) := 'L';
    = dsc$dftb_eid_page_map_element =
      fault_symptom_code (2) := 'P';
    ELSE
    CASEND;

    fault_symptom_code (1) := 'D';

  PROCEND build_fault_symptom_code;
?? OLDTITLE ??
?? NEWTITLE := 'degrade_vector', EJECT ??

{ PURPOSE:
{   This procedure issues a message to the critical window whenever DFT degrades a vector divide unit.
{   It also updates the vector status in the SCB.

  PROCEDURE degrade_vector
    (    element_number: dst$dftb_mrt_element_index);

    VAR
      element_entry: dst$mf_element_table_entry,
      message: string (40),
      processor_id: ost$processor_id;

    get_mf_element_entry (element_number, element_entry);
    message := 'VECTOR DIVIDE DISABLED IN PROCESSOR 00.';
    message (37, 2) := element_entry.element_number_string.value;
    dpp$display_error (message);

    CASE mtv$scb.vector_simulation_control.vector_simulation_attribute OF
    = pmc$vectors_simulated =
      message := 'AFFECTED JOBS WILL CONTINUE EXECUTION.';
    = pmc$vectors_suspended =
      message := 'AFFECTED JOBS WILL BE SUSPENDED.';
    = pmc$vectors_aborted =
      message := 'AFFECTED JOBS WILL BE ABORTED.';
    ELSE
      message := ' ';
    CASEND;
    dpp$display_error (message);

    processor_id := element_entry.element_id.element_number;
    mtp$deconfigure_divide_unit (processor_id);

  PROCEND degrade_vector;
?? OLDTITLE ??
?? NEWTITLE := 'flaw_page', EJECT ??

{ PURPOSE:
{   This procedure calls the appropriate memory manager routine to flaw the page specified in the NRSB entry.

  PROCEDURE flaw_page;

    TYPE
      t$flaw_page = RECORD
        rfu: 0 .. 0ffffffff(16),
        page_number: mmt$page_frame_index,
      RECEND;

    VAR
      buffer_seq_p: ^SEQ ( * ),
      data_length: integer,
      flaw_page_p: ^t$flaw_page,
      nrb_internal_header_p: ^dst$dftb_nrb_internal_header,
      partial_nrb_record_p: ^t$partial_nrb_record,
      seq_entry_pp: ^^SEQ ( * ),
      seq_header: cyt$sequence_pointer,
      skip_data_p: ^SEQ ( * );

    IF v$buffer.data_p = NIL THEN
      RETURN;
    IFEND;

    seq_entry_pp := #LOC(seq_header);
    seq_header.pva := v$buffer.data_p;
    IF v$buffer.data_from_mrb THEN
      seq_header.length := dsv$dftb_data.mrb_length * 8;
    ELSE
      seq_header.length := dsv$dftb_data.nrb_length * 8;
    IFEND;
    seq_header.nextt := 0;
    buffer_seq_p := seq_entry_pp^;

    IF buffer_seq_p = NIL THEN
      RETURN;
    IFEND;

    RESET buffer_seq_p;
    data_length := #SIZE (buffer_seq_p^);
    NEXT partial_nrb_record_p IN buffer_seq_p;
    data_length := data_length - #SIZE (partial_nrb_record_p^);
    WHILE data_length > 0 DO
      NEXT nrb_internal_header_p IN buffer_seq_p;
      IF nrb_internal_header_p^.type_code = dsc$dftb_nrb_ih_flaw_page_num THEN
        NEXT flaw_page_p IN buffer_seq_p;
        mmp$mark_page_flawed (flaw_page_p^.page_number);
        RETURN;
      IFEND;
      NEXT skip_data_p: [[REP (nrb_internal_header_p^.length - 1) OF integer]] IN buffer_seq_p;
      data_length := data_length - (nrb_internal_header_p^.length * 8);
    WHILEND;

  PROCEND flaw_page;
?? OLDTITLE ??
?? NEWTITLE := 'get_mf_element_entry', EJECT ??

{ PURPOSE:
{   This procedure retrieves an entry from the Mainframe element table.

  PROCEDURE get_mf_element_entry
    (    element_number: dst$dftb_mrt_element_index;
     VAR element_entry: dst$mf_element_table_entry);

    VAR
      element_id: dst$mf_element_id,
      entry_index: ost$processor_element_number;

    { Initialize the element entry.

    element_entry.element_id.element_number := 0;
    element_entry.element_id.dft_entry_id := 0;
    element_entry.model_number := 0;
    element_entry.serial_number := 0;
    element_entry.element_number_string.size := 1;
    element_entry.element_number_string.value := ' ';
    element_entry.model_number_string := element_entry.element_number_string;
    element_entry.serial_number_string := element_entry.element_number_string;

    IF dsv$mf_element_table_p <> NIL THEN

      { Find the element number of the error.

      element_id.element_number := element_number DIV 16;
      element_id.dft_entry_id := element_number MOD 16;

     /search_element_table/
      FOR entry_index := LOWERBOUND (dsv$mf_element_table_p^) TO UPPERBOUND (dsv$mf_element_table_p^) DO
        IF dsv$mf_element_table_p^ [entry_index].element_id = element_id THEN
          element_entry := dsv$mf_element_table_p^ [entry_index];
          EXIT /search_element_table/;
        IFEND;
      FOREND /search_element_table/;
    IFEND;

  PROCEND get_mf_element_entry;
?? OLDTITLE ??
?? NEWTITLE := 'process_os_action_code', EJECT ??

{ PURPOSE:
{   This procedure processes the OS action code that is found in the buffer control word.

  PROCEDURE process_os_action_code
    (    element_number: dst$dftb_mrt_element_index;
         buffer_time: t$buffer_time;
         fault_symptom_code: t$fault_symptom_code;
     VAR buffer_cw_p: ^dst$dftb_buffer_control_word;
     VAR buffer_entry_interlocked: boolean);

    VAR
      dft_analysis_code: dst$dftb_dft_analysis_code,
      interlock_previously_set: boolean,
      traps: 0 .. 3;

    i#mtr_disable_traps (traps);

    { Interlock the buffer control word so DFT will not use it while the OS is accessing the word.

    i#test_set_bit (^buffer_cw_p^, dsc$dftb_interlock_bit, interlock_previously_set);
    IF interlock_previously_set THEN
      buffer_entry_interlocked := TRUE;
      i#mtr_restore_traps (traps);
      RETURN;
    IFEND;

   /buffer_interlocked/
    BEGIN

      { Save the error in the System Deadstart Status statistic.

      IF buffer_cw_p^.priority >= dsc$dftb_pri_uncorrected_error THEN
        dsp$mtr_save_mainframe_error (element_number, buffer_time.date_and_time, fault_symptom_code.words);
      IFEND;

      { Return if there are no OS action codes to process.

      IF buffer_cw_p^.os_action_code = dsc$dftb_oac_no_action THEN
        EXIT /buffer_interlocked/;
      IFEND;

      { Retrieve the DFT analysis code and remove the possiblity of multiple errors.

      dft_analysis_code := buffer_cw_p^.dft_analysis_code;
      IF buffer_cw_p^.dft_analysis_code > dsc$dftb_multiple_errors THEN
        dft_analysis_code := dft_analysis_code - dsc$dftb_multiple_errors;
      IFEND;

      record_hardware_status (element_number, buffer_time, fault_symptom_code, buffer_cw_p^.os_action_code,
            dft_analysis_code);
    END /buffer_interlocked/;

    { Set the valid bit to FALSE to indicate that the OS has processed the error and
    { set the interlock bit to FALSE to allow DFT to access the buffer control word again.

    buffer_cw_p^.flags.c180_valid_data := FALSE;
    buffer_cw_p^.flags.interlock := FALSE;
    i#mtr_restore_traps (traps);

  PROCEND process_os_action_code;
?? OLDTITLE ??
?? NEWTITLE := 'record_hardware_status', EJECT ??

{ PURPOSE:
{   This procedure determines what hardware action should be taken depending upon the OS action code
{   and calls the appropriate routine to record the status.

  PROCEDURE record_hardware_status
    (    element_number: dst$dftb_mrt_element_index;
         buffer_time: t$buffer_time;
         fault_symptom_code: t$fault_symptom_code;
         os_action_code: dst$dftb_os_action_code;
         dft_analysis_code: dst$dftb_dft_analysis_code);

    VAR
      console_message: t$console_message_record,
      element_entry: dst$mf_element_table_entry,
      hardware_action: mtt$scb_hardware_status_actions,
      hardware_message: t$hardware_message_type,
      hardware_option: mtt$scb_hardware_status_options,
      ignore_status: syt$monitor_status,
      message_p: ^dpt$top_line_message,
      message_seq_p: ^SEQ ( * ),
      signal: dst$signal_contents;

    { Process the OS action code.

    CASE os_action_code OF
    = dsc$dftb_oac_no_action =
      RETURN;

    = dsc$dftb_oac_environment, dsc$dftb_oac_long_power =
      hardware_action := mtc$scb_hsa_set;
      hardware_option := mtc$scb_long_warning_idle;
      hardware_message := c$hmt_version_3_180_message;

    = dsc$dftb_oac_short_power =
      hardware_action := mtc$scb_hsa_set;
      hardware_option := mtc$scb_short_warning_step;
      hardware_message := c$hmt_version_3_180_message;

    = dsc$dftb_oac_warning_clear =
      hardware_action := mtc$scb_hsa_clear;
      hardware_message := c$hmt_version_3_180_message;
      CASE dft_analysis_code OF
      = dsc$dftb_dac_non_704, dsc$dftb_dac_non_705, dsc$dftb_dac_non_70A =
        hardware_option := mtc$scb_long_warning_idle;
      ELSE
        hardware_option := mtc$scb_short_warning_step;
      CASEND;

    = dsc$dftb_oac_fatal_iou, dsc$dftb_oac_uncorrected_iou, dsc$dftb_oac_memory =
      hardware_action := mtc$scb_hsa_set;
      hardware_option := mtc$scb_hardware_failure_step;
      hardware_message := c$hmt_version_3_180_message;

    = dsc$dftb_oac_uncorrected_memory, dsc$dftb_oac_uncorrected_cpu =
      RETURN;

    = dsc$dftb_oac_170_state_iou =
      hardware_action := mtc$scb_hsa_set;
      hardware_option := mtc$scb_170_status;
      hardware_message := c$hmt_version_3_170_message;

    = dsc$dftb_oac_system_idle, dsc$dftb_oac_180_state_idle =
      hardware_action := mtc$scb_hsa_set;
      hardware_message := c$hmt_180_message;
      CASE dft_analysis_code OF
      = dsc$dftb_dac_non_701, dsc$dftb_dac_non_702, dsc$dftb_dac_non_709 =
        hardware_option := mtc$scb_long_warning_idle;
      ELSE
        hardware_option := mtc$scb_hardware_failure_idle;
      CASEND;

    = dsc$dftb_oac_system_step, dsc$dftb_oac_180_state_step =
      hardware_action := mtc$scb_hsa_set;
      hardware_message := c$hmt_180_message;
      CASE dft_analysis_code OF
      = dsc$dftb_dac_non_703 =
        hardware_option := mtc$scb_short_warning_step;
      ELSE
        hardware_option := mtc$scb_hardware_failure_step;
      CASEND;

    = dsc$dftb_oac_system_resume, dsc$dftb_oac_180_state_resume =
      hardware_action := mtc$scb_hsa_clear;
      hardware_message := c$hmt_180_message;
      CASE dft_analysis_code OF
      = dsc$dftb_dac_non_704, dsc$dftb_dac_non_705, dsc$dftb_dac_non_70A =
        hardware_option := mtc$scb_long_warning_idle;
      ELSE
        hardware_option := mtc$scb_hardware_failure_idle;
      CASEND;

    = dsc$dftb_oac_system_unstep, dsc$dftb_oac_180_state_unstep =
      hardware_action := mtc$scb_hsa_clear;
      hardware_message := c$hmt_180_message;
      CASE dft_analysis_code OF
      = dsc$dftb_dac_non_706 =
        hardware_option := mtc$scb_short_warning_step;
      ELSE
        hardware_option :=mtc$scb_hardware_failure_step;
      CASEND;

    = dsc$dftb_oac_170_state_idle, dsc$dftb_oac_170_state_step =
      hardware_action := mtc$scb_hsa_set;
      hardware_option := mtc$scb_170_status;
      hardware_message := c$hmt_170_message;

    = dsc$dftb_oac_170_state_resume, dsc$dftb_oac_170_state_unstep =
      hardware_action := mtc$scb_hsa_clear;
      hardware_option := mtc$scb_170_status;
      hardware_message := c$hmt_170_message;

    = dsc$dftb_oac_reconfigure_nce =
      get_mf_element_entry (element_number, element_entry);
      mtp$record_noncrit_hdw_status (element_entry.element_id);
      RETURN;

    = dsc$dftb_oac_vector_degrade =
      degrade_vector (element_number);
      RETURN;

    = dsc$dftb_oac_element_degrade =
      signal.identifier := dsc$deadstart_signal;
      signal.contents.kind := dsc$signal_post_operator_action;
      mtp$get_date_time_at_timestamp (#FREE_RUNNING_CLOCK (0), signal.contents.poa_data.date_time);
      signal.contents.poa_data.kind := dsc$signal_poa_sys_in_degrade;
      tmp$send_signal (tmv$system_job_monitor_gtid, signal.signal, ignore_status);
      RETURN;

    = dsc$dftb_oac_flaw_cm_page =
      flaw_page;
      RETURN;

    = dsc$dftb_oac_handle_pp_hang =
      dsp$mtr_handle_pp_hang (element_number, v$buffer.data_from_mrb, v$buffer.data_p);
      RETURN;

    = dsc$dftb_oac_handle_bit_57 =
      dsp$mtr_handle_bit_57 (element_number);
      RETURN;

    ELSE
      RETURN;
    CASEND;

    retrieve_console_message (element_number, buffer_time, fault_symptom_code, hardware_message,
          dft_analysis_code, console_message);

    message_seq_p := #SEQ (console_message);
    RESET message_seq_p;
    NEXT message_p IN message_seq_p;
    mtp$record_critical_hdw_status (hardware_option, hardware_action, message_p^);

  PROCEND record_hardware_status;
?? OLDTITLE ??
?? NEWTITLE := 'retrieve_console_message', EJECT ??

{ PURPOSE:
{   This procedure retrieves the correct message to be displayed on the system console for the error.
{   The code supports the version 3 message structure.

  PROCEDURE retrieve_console_message
    (    element_number: dst$dftb_mrt_element_index;
         buffer_time: t$buffer_time;
         fault_symptom_code: t$fault_symptom_code;
         hardware_message: t$hardware_message_type;
         dft_analysis_code: dst$dftb_dft_analysis_code;
     VAR console_message: t$console_message_record);

    VAR
      element_entry: dst$mf_element_table_entry,
      message: string (40),
      message_index: 1 .. c$number_of_hardware_messages;

    { Retrieve the element id.

    get_mf_element_entry (element_number, element_entry);

    { Initialize the console message.

    console_message.hours := ' ';
    console_message.period_1 := '.';
    console_message.minutes := ' ';
    console_message.period_2 := '.';
    console_message.seconds := ' ';
    console_message.blank_1 := ' ';
    console_message.error_string := 'ERR=';
    console_message.blank_2 := ' ';
    console_message.code := ' ';

    { Put the time in the console message.

    IF buffer_time.time = 0 THEN
      console_message.period_1 := ' ';
      console_message.period_2 := ' ';
    ELSE
      console_message.hours (2) := CHR ((buffer_time.hours MOD 16) + ORD ('0'));
      console_message.hours (1) := CHR (((buffer_time.hours DIV 16) MOD 16) + ORD ('0'));
      console_message.minutes (2) := CHR ((buffer_time.minutes MOD 16) + ORD ('0'));
      console_message.minutes (1) := CHR (((buffer_time.minutes DIV 16) MOD 16) + ORD ('0'));
      console_message.seconds (2) := CHR ((buffer_time.seconds MOD 16) + ORD ('0'));
      console_message.seconds (1) := CHR (((buffer_time.seconds DIV 16) MOD 16) + ORD ('0'));
    IFEND;

    { Put the fault symptom code in the console message.

    IF ((fault_symptom_code.code_1 = 0) AND (fault_symptom_code.code_2 = 0)) OR
          (fault_symptom_code.code_string = ' ') THEN
      build_fault_symptom_code (dft_analysis_code, element_entry, console_message.code);
    ELSE
      console_message.code := fault_symptom_code.code_string;
    IFEND;

    { Put the English message in the console message.

    message := ' ';

   /retrieve_hardware_message/
    FOR message_index := 1 TO c$number_of_hardware_messages DO
      IF v$dft_hardware_messages [message_index].dft_analysis_code = dft_analysis_code THEN
        message := v$dft_hardware_messages [message_index].message;
        IF v$dft_hardware_messages [message_index].insert_number_location > 0 THEN
          message (v$dft_hardware_messages [message_index].insert_number_location, 2) :=
                element_entry.element_number_string.value;
        IFEND;
        EXIT /retrieve_hardware_message/;
      IFEND;
    FOREND /retrieve_hardware_message/;

    IF (hardware_message = c$hmt_version_3_170_message) OR (hardware_message = c$hmt_170_message) THEN
      console_message.english_string (1, 6) := '170 - ';
      console_message.english_string (7, *) := message;
    ELSE
      console_message.english_string := message;
    IFEND;

  PROCEND retrieve_console_message;
?? OLDTITLE ??
?? NEWTITLE := 'setup_variables_to_dft_block', EJECT ??

{ PURPOSE:
{   This procedure retrieves the necessary information from the DFT block.  It creates pointers
{   to the various areas in the block.  At version 4, the pointers are retrieved through R pointers
{   in the DFT block.  Before version 4, not all of the areas had R pointers, several of the areas
{   existed immediately after the R pointer words in the DFT block.  If a particular area in the DFT
{   block is damaged, the pointer to that area will be set to NIL so that the OS will not attempt
{   to access the area.

  PROCEDURE setup_variables_to_dft_block;

    VAR
      buffer_cw_p: ^dst$dftb_buffer_control_word,
      cst_p: ^ost$cpu_state_table,
      dft_block_seq_p: ^SEQ ( * ),
      dft_block_size: integer,
      dft_cw_p: ^dst$dftb_control_word,
      monitor_mps: integer,
      nrb_hw_p: ^dst$dftb_buffer_header_word,
      number_of_pointer_words: dst$dftb_cw_pointer_words,
      nve_req_base_p: ^cell,
      r_pointer_words_p: ^dst$dftb_r_pointer_words,
      seq_entry_pp: ^^SEQ ( * ),
      seq_header: cyt$sequence_pointer,
      seq_p: ^SEQ ( * ),
      ssb_hw_p: ^dst$dftb_buffer_header_word;

    { Setup a pointer to the address space of the first CPU started.

    mtv$monitor_xp_slot_pointers.slot_1_p := ^mtv$monitor_exchange_package;
    i#real_memory_address (mtv$monitor_xp_slot_pointers.slot_1_p, monitor_mps);
    mtp$cst_p (cst_p);
    cst_p^.monitor_mps := monitor_mps;

    { Setup a pointer to the address space of the second CPU to be started.

    mtv$monitor_xp_slot_pointers.slot_2_p := #ADDRESS (#RING (mtv$monitor_xp_slot_pointers.slot_1_p),
          #SEGMENT (mtv$monitor_xp_slot_pointers.slot_1_p), osc$monitor_stack_mult);

    v$dftb_data_retrieved := TRUE;

    { Initialize the buffer data.

    v$dftb_mrb_cw_offset := 0;
    v$dftb_mrb_offset := 0;
    dsv$dftb_data.number_of_mrbs := 0;
    dsv$dftb_data.mrb_length := 0;

    v$dftb_ssb_offset := 0;
    dsv$dftb_data.ssb_length := 0;

    v$dftb_nrb_offset := 0;
    dsv$dftb_data.number_of_nrbs := 0;
    dsv$dftb_data.nrb_length := 0;

    v$dftb_mdb_p := NIL;
    dsv$dftb_data.number_of_mdbs := 0;
    dsv$dftb_data.mdb_length := 0;

    dsv$dfts_control_word_p := NIL;
    dsv$dftb_nve_req_buffer_p := NIL;

    dsv$dftb_data.revision_level := 0;
    dsv$dftb_data.secded_id_table_length := 0;

    { If the number of pointer words is not greater than one, the DFT buffer is damaged and should
    { not be accessed.  This is accomplished by setting all of the DFT buffer pointers to NIL.

    IF mtv$dft_block_p^.pointer_words <= 1 THEN
      RETURN;
    IFEND;

    { Retrieve the actual number of pointer words.  The value in the DFT control word also includes
    { the DFT control word.

    number_of_pointer_words := mtv$dft_block_p^.pointer_words - 1;

    { Retrieve the revision level.

    dsv$dftb_data.revision_level := mtv$dft_block_p^.revision_level;

    { Retrieve the size of the DFT block data that is contiguous.

    IF dsv$dftb_data.revision_level <= dsc$dftb_revision_level_3 THEN
      dft_block_size := #SIZE (mtv$dft_block_p^) + (number_of_pointer_words * 8) +
            (mtv$dft_block_p^.number_of_mrbs * 8);
    ELSE
      dft_block_size := #SIZE (mtv$dft_block_p^) + (number_of_pointer_words * 8);
    IFEND;

    { Create a sequence pointer to the DFT block.

    seq_entry_pp := #LOC (seq_header);
    seq_header.pva := mtv$dft_block_p;
    seq_header.length := dft_block_size;
    seq_header.nextt := 0;
    dft_block_seq_p := seq_entry_pp^;
    RESET dft_block_seq_p;

    { Get the DFT control word and the DFT buffer pointer words from the DFT buffer.

    NEXT dft_cw_p IN dft_block_seq_p;
    NEXT r_pointer_words_p: [1 .. number_of_pointer_words] IN dft_block_seq_p;

    { Retrieve the length of the SECDED ID table.

    IF number_of_pointer_words >= dsc$dftb_rpw_secded_id THEN
      dsv$dftb_data.secded_id_table_length := r_pointer_words_p^ [dsc$dftb_rpw_secded_id].length;
    IFEND;

    { Find the offset of the Maintenance Register Buffer from the pointer in the DFT block.

    IF number_of_pointer_words >= dsc$dftb_rpw_mrb THEN
      dsp$convert_r_pointer_to_seq_p (r_pointer_words_p^ [dsc$dftb_rpw_mrb], mtv$dft_block_p, seq_p);
      v$dftb_mrb_offset := #OFFSET (seq_p);
    IFEND;

    { Find the offset of the Maintenance Register Buffer Controlwords from the pointer in the DFT block.

    IF (dft_cw_p^.number_of_mrbs > 0) AND (dft_cw_p^.mrb_length > 0) THEN
      dsv$dftb_data.number_of_mrbs := dft_cw_p^.number_of_mrbs;
      dsv$dftb_data.mrb_length := dft_cw_p^.mrb_length;
      IF dsv$dftb_data.revision_level <= dsc$dftb_revision_level_3 THEN
        NEXT buffer_cw_p IN dft_block_seq_p;
        v$dftb_mrb_cw_offset := #OFFSET (buffer_cw_p);
      ELSE
        IF number_of_pointer_words >= dsc$dftb_rpw_mrb_cw THEN
          dsp$convert_r_pointer_to_seq_p (r_pointer_words_p^ [dsc$dftb_rpw_mrb_cw], mtv$dft_block_p, seq_p);
          v$dftb_mrb_cw_offset := #OFFSET (seq_p);
        IFEND;
      IFEND;
    IFEND;

    { Find the offset of the Supportive Status Buffer from the pointer in the DFT block.

    IF number_of_pointer_words >= dsc$dftb_rpw_ssb THEN
      dsp$convert_r_pointer_to_seq_p (r_pointer_words_p^ [dsc$dftb_rpw_ssb], mtv$dft_block_p, seq_p);
      ssb_hw_p := #ADDRESS (1, #SEGMENT (mtv$dft_block_p), #OFFSET (seq_p));
      IF ssb_hw_p^.buffer_size > 0 THEN
        dsv$dftb_data.ssb_length := ssb_hw_p^.buffer_size;
        v$dftb_ssb_offset := #OFFSET (seq_p) + #SIZE (ssb_hw_p^);
      IFEND;
    IFEND;

    { Find the offset of the Non Register Data Buffer from the pointer in the DFT block.

    IF number_of_pointer_words >= dsc$dftb_rpw_nrb THEN
      dsp$convert_r_pointer_to_seq_p (r_pointer_words_p^ [dsc$dftb_rpw_nrb], mtv$dft_block_p, seq_p);
      nrb_hw_p := #ADDRESS (1, #SEGMENT (mtv$dft_block_p), #OFFSET (seq_p));
      IF (nrb_hw_p^.number_of_buffers > 0) AND (nrb_hw_p^.buffer_size > 0) THEN
        dsv$dftb_data.number_of_nrbs := nrb_hw_p^.number_of_buffers;
        dsv$dftb_data.nrb_length := nrb_hw_p^.buffer_size;
        v$dftb_nrb_offset := #OFFSET (seq_p) + #SIZE (nrb_hw_p^);
      IFEND;
    IFEND;

    { Create a pointer to the Model Dependent Buffer.

    IF (number_of_pointer_words >= dsc$dftb_rpw_mdb) AND
          (r_pointer_words_p^ [dsc$dftb_rpw_mdb].length > 0) THEN
      dsp$convert_r_pointer_to_seq_p (r_pointer_words_p^ [dsc$dftb_rpw_mdb], mtv$dft_block_p, seq_p);
      dsv$dftb_data.number_of_mdbs := r_pointer_words_p^ [dsc$dftb_rpw_mdb].length;
      NEXT v$dftb_mdb_p: [0 .. (dsv$dftb_data.number_of_mdbs - 1)] IN seq_p;
      IF v$dftb_mdb_p^ [0].length > 0 THEN
        dsv$dftb_data.mdb_length := v$dftb_mdb_p^ [0].length;
      ELSE
        v$dftb_mdb_p := NIL;
      IFEND;
    IFEND;

    { Create a pointer to the Secondary DFT buffer control word.

    IF (number_of_pointer_words >= dsc$dftb_rpw_dft_secondary) AND
          (r_pointer_words_p^ [dsc$dftb_rpw_dft_secondary].length > 0) THEN
      dsp$convert_r_pointer_to_seq_p (r_pointer_words_p^ [dsc$dftb_rpw_dft_secondary],
            mtv$dft_block_p, seq_p);
      NEXT dsv$dfts_control_word_p IN seq_p;
    IFEND;

    { Create a pointer to the NOS/VE Request Buffer.

    IF (number_of_pointer_words >= dsc$dftb_rpw_nosve_buffer) AND
          (r_pointer_words_p^ [dsc$dftb_rpw_nosve_buffer].length > 0) THEN
      IF dsv$mainframe_type = dsc$mt_2000_mainframe THEN
        nve_req_base_p := mtv$dft_block_p;
      ELSE
        nve_req_base_p := #ADDRESS (1, dsc$ssr_segment_number, dsc$ssr_offset);
      IFEND;
      dsp$convert_r_pointer_to_seq_p (r_pointer_words_p^ [dsc$dftb_rpw_nosve_buffer], nve_req_base_p, seq_p);
      NEXT dsv$dftb_nve_req_buffer_p IN seq_p;
    IFEND;

  PROCEND setup_variables_to_dft_block;
?? OLDTITLE ??
?? NEWTITLE := 'dsp$access_logging_data', EJECT ??

{ PURPOSE:
{   This procedure is called from the logging routine in job template to access the DFT buffer or the system
{   message buffer.  The logging routine (stored in dsm$log_system_messages) must make a call to monitor to
{   retrieve an error buffer in the DFT buffer or data in the system message buffer.  This procedure is also
{   called to set the bit in the DFT control block to tell DFT that the top of hour has occurred.

  PROCEDURE [XDCL] dsp$access_logging_data
    (VAR rb: dst$rb_logging_request);

    VAR
      index: integer,
      interlock_previously_set: boolean,
      message_recorded: boolean,
      new_sys_msg_data_p: ^SEQ ( * ),
      new_sys_msg_header_p: ^dst$system_message_header,
      old_sys_msg_data_p: ^SEQ ( * ),
      old_sys_msg_header_p: ^dst$system_message_header,
      save_add_data_seq_p: ^SEQ ( * );

    CASE rb.action OF

    = dsc$rla_dft_setup_variables =
      IF NOT v$dftb_data_retrieved THEN
        setup_variables_to_dft_block;
      IFEND;

    = dsc$rla_dft_retrieve_dfts_cw =
      IF NOT v$dftb_data_retrieved THEN
        setup_variables_to_dft_block;
      IFEND;
      IF dsv$dfts_control_word_p <> NIL THEN
        rb.dftb_dfts_control_word := dsv$dfts_control_word_p^;
      ELSE
        rb.dftb_dfts_control_word.dft_verification := FALSE;
      IFEND;

    = dsc$rla_dft_access_buffer_entry =
      access_buffer_entry (rb);

    = dsc$rla_dft_log_top_of_hour =

      { Clear out the DUE count in the CST; otherwise the system could shut down a CPU (or worse, abort) if
      {  number of occasional DUEs exceeded the DUE threshold.

      FOR index := 0 TO osc$maximum_processor_number DO
        mtv$cst0 [index].due_count := 0;
      FOREND;

      { Do the same for the 170 DUE counters.

      mtv$170_due_info.due_count := 0;
      mtv$170_due_info.aborted_job_count := 0;

      { Every top of hour a bit must be set in the DFT control word to tell DFT that the top of hour
      { has occurred.  DFT logs some data when this bit is set and then clears the bit.

      IF dsv$turn_dft_logging_off THEN
        RETURN;
      IFEND;

      i#test_set_bit (^mtv$dft_block_p^, dsc$dftb_cw_zero_counters_bit, interlock_previously_set);

    = dsc$rla_sys_msg_add_message =

      { Put a message on the circular buffer.

      RESET rb.sys_msg_add_data_seq_p;
      WHILE i#current_sequence_position (rb.sys_msg_add_data_seq_p) < #SIZE (rb.sys_msg_add_data_seq_p^) DO
        NEXT new_sys_msg_header_p IN rb.sys_msg_add_data_seq_p;
        NEXT new_sys_msg_data_p: [[REP new_sys_msg_header_p^.message_size OF cell]]
              IN rb.sys_msg_add_data_seq_p;
        dsp$report_system_message (new_sys_msg_data_p, new_sys_msg_header_p^.message_type,
              new_sys_msg_header_p^.message_level, message_recorded);
      WHILEND;

    = dsc$rla_sys_msg_get_message =

      { When the logging procedure makes a request for data from the buffer, the 'add' and 'remove'
      { pointers are copied to the request area and logging procedures use the copies of the pointers
      { to access the buffer.  After all the data has been logged, another call is made to move the
      { 'actual remove' pointer to the position of the saved 'copied add' pointer thus destroying
      { access to that data since it has been logged.

      tmp$new_set_lock (v$sys_msg_buffer_lock);
      IF rb.sys_msg_clear_buffer THEN
        dsv$sys_msg_buffer_ptrs.remove_data_seq_p := rb.sys_msg_add_data_seq_p;
        dsv$sys_msg_buffer_desc_p^.remove_data_ptr_offset :=
              i#current_sequence_position (dsv$sys_msg_buffer_ptrs.remove_data_seq_p);
        v$sys_msg_buffer_is_full := FALSE;
      IFEND;
      rb.sys_msg_add_data_seq_p := dsv$sys_msg_buffer_ptrs.add_data_seq_p;
      rb.sys_msg_remove_data_seq_p := dsv$sys_msg_buffer_ptrs.remove_data_seq_p;
      tmp$new_clear_lock (v$sys_msg_buffer_lock);

    = dsc$rla_sys_msg_enlarge_buffer =

      { Move the data from the old System Message buffer to the new enlarged System Message buffer
      { and set all the System Message buffer variables up to point to the new enlarged buffer.

      tmp$new_set_lock (v$sys_msg_buffer_lock);
      save_add_data_seq_p := rb.sys_msg_add_data_seq_p;
      dsv$sys_msg_buffer_size := rb.sys_msg_new_buffer_size;
      dsv$sys_msg_buffer_desc_p^.cm_start_of_buffer_p := rb.sys_msg_add_data_seq_p;
      dsv$sys_msg_buffer_desc_p^.sys_msg_buffer_size := rb.sys_msg_new_buffer_size;

      WHILE (i#current_sequence_position (dsv$sys_msg_buffer_ptrs.remove_data_seq_p) <
            i#current_sequence_position (dsv$sys_msg_buffer_ptrs.add_data_seq_p)) DO
        NEXT old_sys_msg_header_p IN dsv$sys_msg_buffer_ptrs.remove_data_seq_p;
        IF old_sys_msg_header_p^.message_size <> 0 THEN
          NEXT old_sys_msg_data_p: [[REP old_sys_msg_header_p^.message_size OF cell]] IN
                dsv$sys_msg_buffer_ptrs.remove_data_seq_p;
          NEXT new_sys_msg_header_p IN rb.sys_msg_add_data_seq_p;
          NEXT new_sys_msg_data_p: [[REP old_sys_msg_header_p^.message_size OF cell]] IN
                rb.sys_msg_add_data_seq_p;
          new_sys_msg_header_p^ := old_sys_msg_header_p^;
          new_sys_msg_data_p^ := old_sys_msg_data_p^;
        IFEND;
      WHILEND;

      dsv$sys_msg_buffer_ptrs.add_data_seq_p := rb.sys_msg_add_data_seq_p;
      dsv$sys_msg_buffer_ptrs.remove_data_seq_p := save_add_data_seq_p;
      dsv$sys_msg_buffer_desc_p^.add_data_ptr_offset :=
            i#current_sequence_position (dsv$sys_msg_buffer_ptrs.add_data_seq_p);
      dsv$sys_msg_buffer_desc_p^.remove_data_ptr_offset :=
            i#current_sequence_position (dsv$sys_msg_buffer_ptrs.remove_data_seq_p);
      tmp$new_clear_lock (v$sys_msg_buffer_lock);

    ELSE
    CASEND;

  PROCEND dsp$access_logging_data;
?? OLDTITLE ??
?? NEWTITLE := 'dsp$process_dft_entry', EJECT ??

{ PURPOSE:
{   This procedure is called if the valid_180_error bit is set in the DFT buffer.  This procedure
{   checks each buffer control word for a possible error and processes it if one exists.  It then
{   clears the valid_180_error bit and calls the logging routine via a system flag.

  PROCEDURE [XDCL] dsp$process_dft_entry;

    VAR
      buffer_cw_p: ^dst$dftb_buffer_control_word,
      buffer_entry_interlocked: boolean,
      buffer_time: t$buffer_time,
      cw_index: dst$dftb_element_size,
      dft_analysis_code: dst$dftb_dft_analysis_code,
      element_number: dst$dftb_mrt_element_index,
      fault_symptom_code: t$fault_symptom_code,
      nrb_p: ^t$partial_nrb_record,
      ssb_p: ^t$partial_ssb_record,
      status: syt$monitor_status;

    IF dsv$turn_dft_logging_off THEN
      RETURN;
    IFEND;

    WHILE NOT mtv$dft_block_p^.c180_error DO

      { In the future, it is possible that this procedure will be called without this bit being
      { set.  In this case, the system noticed the error before DFT.  The error will be picked
      { up by DFT.  It is important to wait here until DFT notices the error, logs the data and
      { sets this bit so that the data can be picked up by the OS and processed.

    WHILEND;
    mtv$dft_block_p^.c180_error := FALSE;

    IF NOT v$dftb_data_retrieved THEN
      setup_variables_to_dft_block;
    IFEND;

    buffer_entry_interlocked := FALSE;
    element_number := 0;
    buffer_time.time := 0;
    fault_symptom_code.code_1 := 0;
    fault_symptom_code.code_2 := 0;
    v$buffer.data_p := NIL;

    { Process the maintenance register buffer data.

    IF v$dftb_mrb_cw_offset <> 0 THEN
      FOR cw_index := 1 TO dsv$dftb_data.number_of_mrbs DO
        buffer_cw_p := #ADDRESS (1, #SEGMENT (mtv$dft_block_p),
              v$dftb_mrb_cw_offset + ((cw_index - 1) * #SIZE (dst$dftb_buffer_control_word)));
        IF buffer_cw_p^.flags.c180_valid_data THEN
          v$buffer.data_p := #ADDRESS (1, #SEGMENT (mtv$dft_block_p),
                v$dftb_mrb_offset + (buffer_cw_p^.offset * 8));
          v$buffer.data_from_mrb := TRUE;
          IF v$dftb_ssb_offset <> 0 THEN
            ssb_p := #ADDRESS (1, #SEGMENT (mtv$dft_block_p),
                  v$dftb_ssb_offset + ((cw_index - 1) * (dsv$dftb_data.ssb_length * 8)));
            element_number := ssb_p^.information_word.element_number;
            buffer_time := ssb_p^.buffer_time;
            fault_symptom_code := ssb_p^.fault_symptom_code;
          IFEND;
          IF  buffer_cw_p^.dft_analysis_code < 999(16) THEN
          process_os_action_code (element_number, buffer_time, fault_symptom_code, buffer_cw_p,
                buffer_entry_interlocked);
          IFEND;
        IFEND;
      FOREND;
    IFEND;

    { Process the non register buffer data.

    IF v$dftb_nrb_offset <> 0 THEN
      FOR cw_index := 1 TO dsv$dftb_data.number_of_nrbs DO
        nrb_p := #ADDRESS (1, #SEGMENT (mtv$dft_block_p),
              v$dftb_nrb_offset + ((cw_index - 1) * (dsv$dftb_data.nrb_length * 8)));
        buffer_cw_p := ^nrb_p^.control_word;
        IF buffer_cw_p^.flags.c180_valid_data THEN
          v$buffer.data_p := ^nrb_p^;
          v$buffer.data_from_mrb := FALSE;
          IF dsv$mainframe_type = dsc$mt_2000_mainframe THEN
            IF nrb_p^.information_word.cy2000_element = 1(16) THEN
              element_number := dsc$dftb_eid_cpu0_element;
            ELSEIF nrb_p^.information_word.cy2000_element = 2(16) THEN
              element_number := dsc$dftb_eid_cpu1_element;
            ELSEIF nrb_p^.information_word.cy2000_element = 4(16) THEN
              element_number := dsc$dftb_eid_memory_element;
            ELSEIF nrb_p^.information_word.cy2000_element = 8(16) THEN
              element_number := dsc$dftb_eid_iou0_element;
            ELSEIF nrb_p^.information_word.cy2000_element = 10(16) THEN
              element_number := dsc$dftb_eid_iou1_element;
            ELSE
              element_number := dsc$dftb_eid_no_known_element;
            IFEND;
          ELSE
            element_number := nrb_p^.information_word.element_number;
          IFEND;
          buffer_time := nrb_p^.buffer_time;
          fault_symptom_code := nrb_p^.fault_symptom_code;

          IF  buffer_cw_p^.dft_analysis_code < 999(16) THEN
          process_os_action_code (element_number, buffer_time, fault_symptom_code, buffer_cw_p,
                buffer_entry_interlocked);
          IFEND;
        IFEND;
      FOREND;
    IFEND;

    { If a buffer entry was found to be interlocked and the number of attempts to access the interlocked
    { buffer is less then the max, set the DFT control word flag so that after a period of time this
    { routine will be called again and hopefully the interlock will be cleared.  If after the maximum
    { attempts to access an interlocked buffer, assume the interlock is stuck and do not set the bit.

    IF buffer_entry_interlocked AND (v$dftb_buffer_entries_checked < 10) THEN
      v$dftb_buffer_entries_checked := v$dftb_buffer_entries_checked + 1;
      mtv$dft_block_p^.c180_error := TRUE;
    ELSE
      v$dftb_buffer_entries_checked := 0;
    IFEND;

    { Initiate the logging of the error to the engineering log.

    IF dsv$record_errors THEN
      tmp$set_system_flag (tmv$system_job_monitor_gtid, dsc$log_dft_flag_id, status);
    IFEND;

  PROCEND dsp$process_dft_entry;
?? OLDTITLE ??
?? NEWTITLE := 'dsp$report_system_message', EJECT ??

{ PURPOSE:
{   This procedure logs data into the engineering log.  This procedure stores the data on a
{   circular buffer and notifies the logging routine via a system flag that there is data to log.

  PROCEDURE [XDCL] dsp$report_system_message
    (    message_seq_p: ^SEQ ( * );
         message_type: dst$system_logging_types;
         message_level: dst$system_message_levels;
     VAR message_recorded: boolean);

    VAR
      add_data_ptr_offset: integer,
      add_data_seq_p: ^SEQ ( * ),
      buffer_is_full_space_needed: 0 .. dsc$sys_msg_buffer_size,
      buffer_sys_msg_data_p: ^SEQ ( * ),
      buffer_sys_msg_header_p: ^dst$system_message_header,
      end_of_buffer_offset: integer,
      leftover_space_needed: integer,
      logical_end_of_buffer: integer,
      message_data_seq_p: ^SEQ ( * ),
      message_p: ^string (80),
      message_type_p: ^integer,
      remove_data_ptr_offset: integer,
      status: syt$monitor_status,
      system_message_header: dst$system_message_header;

    message_recorded := FALSE;
    tmp$new_set_lock (v$sys_msg_buffer_lock);

   /buffer_lock_set/
    BEGIN

      { Return to the caller if the System Message buffer is not initialized.

      IF NOT dsv$sys_msg_buffer_initialized THEN
        EXIT /buffer_lock_set/;
      IFEND;

      { Return to the caller if the buffer is full, the logging routine has
      { not had enough time to empty the buffer.  The message will be discarded.

      IF v$sys_msg_buffer_is_full THEN
        EXIT /buffer_lock_set/;
      IFEND;

      { Return to the caller if there is no message data to log.

      IF message_seq_p = NIL THEN
        EXIT /buffer_lock_set/;
      IFEND;
      message_data_seq_p := message_seq_p;
      RESET message_data_seq_p;

      { Create the header for the System Message buffer entry.

      system_message_header.size_name := 'SIZE';
      system_message_header.message_size := #SIZE (message_data_seq_p^);
      system_message_header.type_name := 'TYPE';
      system_message_header.message_type := message_type;
      system_message_header.level_name := 'LVEL';
      system_message_header.message_level := message_level;

      { Store the data onto the circular buffer.  The circular buffer is driven by two pointers:
      { an add_data pointer and a remove_data pointer.  Data is added to the buffer via the add_data
      { pointer and removed from the buffer via the remove_data pointer.  Therefore, the data that is
      { after the remove_data pointer and before the add_data pointer is valid data.  If, when adding
      { data to the add_data pointer, it overlaps the remove_data pointer then the buffer is full.
      { Enough space is left on the buffer to state this fact and the data is discarded.

    /save_message/
      BEGIN

        add_data_seq_p := dsv$sys_msg_buffer_ptrs.add_data_seq_p;
        add_data_ptr_offset := i#current_sequence_position (dsv$sys_msg_buffer_ptrs.add_data_seq_p);
        remove_data_ptr_offset := i#current_sequence_position (dsv$sys_msg_buffer_ptrs.remove_data_seq_p);

        { The amount of space needed to store the message is equal to the size of the
        { system message header, the size of the message to be placed on the buffer,
        { the size of the system message header for the 'buffer is full' message, the
        { message type variable discribing the message and the size of the 'buffer is
        { full' message.

        buffer_is_full_space_needed := #SIZE (dst$system_message_header) +
              8 {size of message_type_p^} + 80 {size of message_p^};
        leftover_space_needed := #SIZE (dst$system_message_header) +
              system_message_header.message_size + buffer_is_full_space_needed;

        { The logical end of buffer is the end of the buffer minus a system message header.  This
        { is to allow for a system message header to be added to the end of the physical buffer.
        { This system message header is used to show that the input pointer has wrapped around the
        { circular buffer.

        logical_end_of_buffer := dsv$sys_msg_buffer_size - #SIZE (dst$system_message_header);

        { Check if there is enough space between the add_data pointer and the end of the buffer.

        IF (add_data_ptr_offset + leftover_space_needed) > logical_end_of_buffer THEN

          { There is not enough space between the add_data pointer and the end of the buffer.
          { If the remove_data pointer is between the add_data pointer and the end of the buffer
          { OR The remove_data pointer is so close to the beginning of the buffer that there is
          { no room to store the message then the buffer is full.  The 'buffer is full' message
          { is placed on the buffer and the message is discarded.

          IF (remove_data_ptr_offset > add_data_ptr_offset) OR
                (remove_data_ptr_offset < (leftover_space_needed +
                i#current_sequence_position (dsv$sys_msg_buffer_desc_p^.cm_start_of_buffer_p))) THEN
            v$sys_msg_buffer_is_full := TRUE;
            end_of_buffer_offset := logical_end_of_buffer;
            EXIT /save_message/;
          ELSE

            { The add_data pointer is too close to the bottom of the buffer to add
            { the message.  Place a system message header with message size of zero
            { on the buffer, reset the add_data pointer which causes the buffer to
            { to wrap around.  The system message header with message size of zero
            { is a signal that the buffer has wrapped around.

            NEXT buffer_sys_msg_header_p IN add_data_seq_p;
            buffer_sys_msg_header_p^ := system_message_header;
            buffer_sys_msg_header_p^.message_size := 0;
            RESET add_data_seq_p;
            add_data_ptr_offset := i#current_sequence_position (add_data_seq_p);
          IFEND;
        IFEND;

        { Check if there is enough space between the add_data pointer and remove_data pointer to
        { put the message.  If there is not enough space then the 'buffer is full' message is
        { placed on the buffer and the message is discarded.

        IF (remove_data_ptr_offset > add_data_ptr_offset) AND
              (remove_data_ptr_offset < add_data_ptr_offset + leftover_space_needed) THEN
          v$sys_msg_buffer_is_full := TRUE;
          end_of_buffer_offset := remove_data_ptr_offset;
          EXIT /save_message/;
        IFEND;

        { Enough room exists on the buffer to place the message so the message is placed on the buffer.

        NEXT buffer_sys_msg_header_p IN add_data_seq_p;
        NEXT buffer_sys_msg_data_p: [[REP system_message_header.message_size OF cell]] IN add_data_seq_p;
        buffer_sys_msg_header_p^ := system_message_header;
        buffer_sys_msg_data_p^ := message_data_seq_p^;
        message_recorded := TRUE;

      END /save_message/;

      { If the buffer is full, place a message stating that the buffer is full on the buffer.

      IF v$sys_msg_buffer_is_full AND
            ((add_data_ptr_offset + buffer_is_full_space_needed) <= end_of_buffer_offset) THEN
        NEXT buffer_sys_msg_header_p IN add_data_seq_p;
        NEXT message_type_p IN add_data_seq_p;
        NEXT message_p IN add_data_seq_p;
        buffer_sys_msg_header_p^.size_name := 'SIZE';
        buffer_sys_msg_header_p^.message_size := #SIZE (message_type_p^) + #SIZE (message_p^);
        buffer_sys_msg_header_p^.type_name := 'TYPE';
        buffer_sys_msg_header_p^.message_type := dsc$general_system_message;
        buffer_sys_msg_header_p^.level_name := 'LVEL';
        buffer_sys_msg_header_p^.message_level := dsc$informative_message;
        message_type_p^ := cml$system_informative_message;
        message_p^ := 'The circular system message buffer has overflowed -- some data has been lost';
        message_recorded := TRUE;
      IFEND;

      { Update the System Message buffer descriptive record, saving the
      { offsets of the two pointers.

      dsv$sys_msg_buffer_ptrs.add_data_seq_p := add_data_seq_p;
      dsv$sys_msg_buffer_desc_p^.add_data_ptr_offset :=
            i#current_sequence_position (dsv$sys_msg_buffer_ptrs.add_data_seq_p);
      dsv$sys_msg_buffer_desc_p^.remove_data_ptr_offset :=
            i#current_sequence_position (dsv$sys_msg_buffer_ptrs.remove_data_seq_p);

    END /buffer_lock_set/;
    tmp$new_clear_lock (v$sys_msg_buffer_lock);

    { Set the system job flag to initiate the logging of message data
    { from buffer.  This logging is done in job mode.

    IF dsv$record_errors AND message_recorded THEN
      tmp$set_system_flag (tmv$system_job_monitor_gtid, dsc$retrieve_system_message, status);
    IFEND;

  PROCEND dsp$report_system_message;
?? OLDTITLE ??
MODEND dsm$process_system_messages;
