MODULE mtm$system_control;
?? RIGHT := 110 ??

{
{  PURPOSE:
{     This module is called periodically to do the following:
{        . monitor status of the SMU Control Block (SCB)
{     This module also contains the routines which govern:
{        . the final processing of idle_system
{        . the initial processing of resume_system
{        . step_system
{        . unstep_system
{        . terminate_system
{

?? PUSH (LISTEXT := ON) ??
*copyc cmt$element_state
*copyc dst$mf_element_table_entry
*copyc dst$system_logging_types
*copyc jmt$initiate_system_idle
*copyc mmt$rb_idle_system
*copyc mtt$monitor_lock
*copyc osc$processor_defined_registers
*copyc ost$170_os_type
*copyc oss$mainframe_wired
*copyc oss$mainframe_wired_literal
*copyc ost$cpu_state_table
*copyc ost$date_time
*copyc ost$free_running_clock
*copyc ost$keypoint_control
*copyc ost$stack_frame_save_area
*copyc ost$terminate_continue_stats
*copyc syt$180_idle_code
*copyc syt$monitor_request_code
*copyc tmc$signal_identifiers
*copyc tme$monitor_mode_exceptions
*copyc tmt$system_task_id
?? POP ??
{ Define constants for use within this module.

  CONST
    top_line_msg_size = dpc$console_row_size - 4,
    number_of_processors = 2,
    mtc$check_scb_interval = 1000000, {Interval for calling this routine to check SCB.
    mtc$max_time_to_be_away = 4 * mtc$check_scb_interval;  {maximum nuber of intervals to be away.

{ Define External variables and procedures.

*copyc cmp$enable_all_connections
*copyc dpp$display_error
*copyc dpv$180_operator_action
*copyc dpv$top_window_p
*copyc dsp$advance_ds_sequence_in_mtr
*copyc dsp$mtr_change_bct_flag
*copyc dsp$mtr_save_cause_and_time
*copyc dsp$mtr_save_top_line_message
*copyc dsp$perform_cpu_pp_handshaking
*copyc dsp$report_system_message
*copyc iop$idle_all_paths
*copyc iop$resume_all_paths
*copyc i#mtr_disable_traps
*copyc i#mtr_enable_traps
*copyc i#program_error
*copyc i#mtr_restore_traps
*copyc mmp$include_p_reg_in_dump
*copyc mtp$clear_lock
*copyc mtf$cst_p
*copyc mtp$idle_180
*copyc mtp$interrupt_processor

*copyc mtp$inject_hardware_fault

*copyc mtp$monitor_processor_status
*copyc mtp$set_lock
*copyc mtp$terminate_180
*copyc mtv$cst0
*copyc mtv$dummy_trace_buffer
*copyc mtv$scb
*copyc mtv$idle_message_line
*copyc mtv$ns_xp_p
*copyc mtv$nst_p
*copyc mtv$operator_console_hung
*copyc mtv$processor_mode
*copyc mtv$request_table
*copyc mtv$time_to_check_scb_status
*copyc mtv$max_async_lock_time
*copyc osv$base_system_time
*copyc osv$cpus_physically_configured
*copyc pmp$this_is_a_leap_year
*copyc tmp$flag_all_tasks
*copyc tmp$monitor_ready_system_task

  VAR
    v$leap_year: [STATIC, READ, oss$mainframe_wired_literal] ARRAY [1 .. 12] OF 1 .. 31 :=
          [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],

    v$non_leap_year: [STATIC, READ, oss$mainframe_wired_literal] ARRAY [1 .. 12] OF 1 .. 31 :=
          [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

{ The following variable is explicitly XREF'd here to avoid the common deck's attribute of READ, because this
{ module may have to change the value of this variable.

  VAR
    osv$170_os_type: [XREF] ost$170_os_type;

{ The following variable is used to save an idle code that could not be sent to the
{ system job monitor. The routine mtp$monitor_system_status will keep trying to send the
{ request until it is accepted.

  VAR
    idle_requested: [STATIC] syt$180_idle_code := syc$ic_null;

{ The following variable is used as a lock to serialize processor requests to the SMU.

  VAR
    mtv$smu_request_lock: [STATIC] mtt$monitor_lock := 0;

{ The following variable is set to TRUE when system core initialization is complete.

  VAR
    mtv$sys_core_init_complete: [XDCL, #GATE] boolean := FALSE;

{ The following variable is set to TRUE if we are running in NOS/CPU1 dedicated
{ mode. This implies only NOS runs in CPU 1, VE and NOS share CPU 0.


?? TITLE := 'Types and variables for system control', EJECT ??

*copyc mtt$nosve_control_status

  VAR
    mtv$nosve_control_status: [XDCL, #GATE] mtt$nosve_control_status := [mtc$system_not_idle,
      mtc$system_not_stepped, mtc$trs_system_ready_to_step];

  VAR
    mtv$step_condition_has_occurred: [XDCL] boolean := FALSE;

  VAR
    mtv$automatic_unstep_resume: [XDCL, #GATE] boolean := TRUE;

  VAR
    mtv$short_warning_seen: [XDCL] integer := 0;

  VAR
    mtv$time_to_call_handshaking: [XDCL] integer := 0;


{ The following are monitor-job flags for date and time handling.
*if false
  VAR
    iov$reject_address_buffer_full: [XREF] integer,
    iov$reject_interlock_set: [XREF] integer,
    iov$reject_requests_full: [XREF] integer,
    iov$reject_unit_queue_limit: [XREF] integer,

    iov$total_queue_calls: [XREF] integer,
    iov$read_priority_invoked: [XREF] integer,
    iov$actual_requests_resolved: [XREF] integer,

{   mmv$sq_rcount: [XREF] integer,
{   mmv$sq_mcount: [XREF] integer,
{   mmv$jws_rcount: [XREF] integer,
{   mmv$jws_mcount: [XREF] integer,

{   mtv$status_cycle_total: [XDCL, #GATE, oss$mainframe_wired] integer := 0,
{   mtv$status_cycle_stop: [XDCL,#GATE, oss$mainframe_wired] integer := 0,
{   mtv$statistics_stamp: integer;
*ifend
  VAR
    osv$date_time_update: [XDCL,#GATE, oss$mainframe_wired] boolean := FALSE;

  VAR
    mtv$fault_injection_flag: [XDCL,#GATE] boolean := FALSE,
    mtv$target_processor_id: [XDCL, #GATE] ost$processor_id := 0,
    mtv$check_if_hdw_cleared_before: [XDCL] boolean := FALSE;

  VAR
    mtv$idle_step_message: [XDCL, #GATE] dpt$top_line_message := ' ';

?? OLDTITLE, NEWTITLE := 'MTP$RECORD_CRITICAL_HDW_STATUS', EJECT ??
{------------------------------------------------------------------------------------------
{  PURPOSE:
{  This procedure is called to update the hardware status of the SCB,
{  and to store the message sent by the logger.
{------------------------------------------------------------------------------------------

  PROCEDURE [XDCL] mtp$record_critical_hdw_status
    (    hardware_option: mtt$scb_hardware_status_options;
         hardware_action: mtt$scb_hardware_status_actions;
         hardware_message: dpt$top_line_message);


    IF hardware_action = mtc$scb_hsa_set THEN
      IF mtv$scb.hardware_status [hardware_option] = mtc$scb_max_hardware_status THEN
{       mtp$store_informative_message ('System note: Maximum hardware errors count exceeded.');
        RETURN;
      IFEND;

      IF NOT mtv$scb.hardware_status_messages [hardware_option].message_read THEN
        mtv$scb.hardware_status_messages [hardware_option].message := hardware_message;
        mtv$scb.hardware_status_messages [hardware_option].message_read := TRUE;
      IFEND;
      IF hardware_option = mtc$scb_short_warning_step THEN
        mtv$scb.hardware_status [hardware_option] := 1;
      ELSE
        mtv$scb.hardware_status [hardware_option] := mtv$scb.hardware_status [hardware_option] + 1;
      IFEND;
    ELSE {hardware_action = mtc$scb_hsa_clear}
      IF (mtv$scb.hardware_status [hardware_option] = 0) AND (hardware_option <> mtc$scb_short_warning_step)
            THEN
        mtp$store_informative_message ('System note: Hardware error CLEAR without corresponding SET.');
        RETURN;
      IFEND;

      IF NOT mtv$scb.hardware_status_messages [hardware_option].message_read THEN
        mtv$scb.hardware_status_messages [hardware_option].message := hardware_message;
        mtv$scb.hardware_status_messages [hardware_option].message_read := TRUE;
      IFEND;
      IF hardware_option = mtc$scb_short_warning_step THEN
        mtv$scb.hardware_status [hardware_option] := 0;
      ELSE
        mtv$scb.hardware_status [hardware_option] := mtv$scb.hardware_status [hardware_option] - 1;
      IFEND;
    IFEND;

  PROCEND mtp$record_critical_hdw_status;
?? OLDTITLE ??
?? NEWTITLE := 'mtp$record_noncrit_hdw_status', EJECT ??

{  PURPOSE:
{    This procedure is called to update the hardware status of the CPU system elements.  It is called by the
{    code that processes OS Actions from the DFT PP.  DFT has noticed that a CPU is no longer operating
{    correctly.

  PROCEDURE [XDCL] mtp$record_noncrit_hdw_status
    (    element_id: dst$mf_element_id);

    TYPE
      t$id_or_element = PACKED RECORD
        CASE boolean OF
        = TRUE =
          fill: 0 .. 1f(16),
          processor_id: ost$processor_id,
        = FALSE =
          element_number: dst$mf_element_number,
        CASEND,
      RECEND;

    VAR
      id_or_element: t$id_or_element;

    IF element_id.dft_entry_id = dsc$dftb_eid_cpu0_element THEN
      id_or_element.element_number := element_id.element_number;
      IF id_or_element.processor_id IN mtv$scb.cpus.logically_on THEN
        mtv$cst0 [id_or_element.processor_id].reason_for_current_state := osc$cdsr_downed_by_dft;
        mtv$scb.cpus.hdw_state_change := mtv$scb.cpus.hdw_state_change +
              $ost$processor_id_set [id_or_element.processor_id];
        mtv$cst0 [id_or_element.processor_id].next_processor_state := cmc$down;
      IFEND;
    IFEND;

  PROCEND mtp$record_noncrit_hdw_status;
?? OLDTITLE, NEWTITLE := 'MTP$MONITOR_SYSTEM_STATUS', EJECT ??
{------------------------------------------------------------------------------------------
{  PURPOSE:
{  This procedure is called periodically to check the status of the SCB. If a
{  condition has occurred that requires idling of the 180 environment, a
{  signal is sent to the system job monitor to begin system idle (for R1,
{  all jobs are aborted).
{  If the system is stepped, this procedure checks the SCB for changes in
{  DFT-detected hardware errors and notifies the operator of the change:
{        . the error can occur
{        . the error can clear
{  If there are no hardware errors the system will resume execution
{  automatically or it will inform the operator how to restart the system
{  manually.
{------------------------------------------------------------------------------------------

  PROCEDURE [XDCL] mtp$monitor_system_status
    (    dummy: ^cell;
         cst_p: ^ost$cpu_state_table);

    CONST
      one_second = 1000000, {Used in short_warning processing}
      ten_seconds = 10000000;
    TYPE
      hardware_errors = SET OF mtt$scb_hardware_status_options,
      monitor_mask = RECORD
        CASE boolean OF
        = FALSE =
          mask: ost$monitor_conditions,
        = TRUE =
          int: 0 .. 0ffff(16),
        CASEND,
      RECEND;

    VAR
      previous_hardware_errors: [XDCL, STATIC] hardware_errors := $hardware_errors [ ],
      current_hardware_errors: [XDCL, STATIC] hardware_errors := $hardware_errors [ ],
      cleared_hardware_errors: [XDCL, STATIC] hardware_errors := $hardware_errors [ ],
      new_hardware_errors: [XDCL, STATIC] hardware_errors := $hardware_errors [ ],
      hardware_status: mtt$scb_hardware_status,
      hardware_index: mtt$scb_hardware_status_options,
      local_monitor_conditions: monitor_mask,
      local_monitor_mask: monitor_mask,
      short_warning_acknowledged: [STATIC] boolean := FALSE,
      i: ost$logical_processor_id,
      pid: ost$processor_id,
      idle_code: syt$180_idle_code,
      ignore_status: syt$monitor_status,
      j: integer,
      clear_message: dpt$top_line_message,
      nos_170_message: dpt$top_line_message;


{ Check for fault injection request.
     IF mtv$fault_injection_flag THEN
      mtv$fault_injection_flag := FALSE;
      mtp$inject_hardware_fault ( mtv$target_processor_id);
       RETURN;
     IFEND;


{ Check for a date/time update.  Set by PP DFT.

    IF mtv$nst_p^.d7st.operator_date_time_update THEN
      mtv$nst_p^.d7st.operator_date_time_update := FALSE;

{ Set the OS date/time flag.  The operator task will read this flag.

      osv$date_time_update := TRUE;
    IFEND;

{ Check for special statistics reset }

{   IF (mtv$statistics_stamp - #free_running_clock(0)) < 0 THEN
{   FOR j := LOWERBOUND (mtv$request_table) TO UPPERBOUND (mtv$request_table) DO
{     mtv$request_table [j].max_time := 0;
{   FOREND;
{
{   FOR j := LOWERBOUND (mtv$request_table) TO UPPERBOUND (mtv$request_table) DO
{     mtv$request_table [j].total_cpu_time := 0;
{   FOREND;
{   mtv$statistics_stamp := #free_running_clock(0) + ten_seconds;
{   mtv$status_cycle_total := #free_running_clock(0)- mtv$status_cycle_stop;
{   mtv$status_cycle_stop := #free_running_clock(0);
{
{   iov$reject_address_buffer_full := 0;
{   iov$reject_interlock_set := 0;
{   iov$reject_requests_full := 0;
{   iov$reject_unit_queue_limit := 0;
{
{   mmv$sq_mcount := 0;
{   mmv$sq_rcount := 0;
{   mmv$jws_rcount := 0;
{   mmv$jws_mcount := 0;
{
{
{   IFEND;
{ Check CPU/PP handshaking.

    IF (mtv$time_to_call_handshaking - #free_running_clock (0)) < 0 THEN
      dsp$perform_cpu_pp_handshaking;
    IFEND;

{ Update time that this routine should be called again.

    mtv$time_to_check_scb_status := #free_running_clock (0) + mtc$check_scb_interval;
    mtv$max_async_lock_time := #free_running_clock (0) + mtc$max_time_to_be_away;




{ Check for a KILL 180 request.

    mtv$scb.kill_180 := mtv$nst_p^.d7st.drop_ve_flag;

    IF (mtv$nosve_control_status.step_state = mtc$system_not_stepped) AND
          (cst_p^.cpu_state.current_state = osc$cpu_running) THEN

      IF mtv$step_condition_has_occurred THEN
        mtv$step_condition_has_occurred := FALSE;
        tmp$flag_all_tasks (osc$system_unstep_resume_flag, ignore_status);
      IFEND;

{ If a short warning occurred during an UNSTEP sequence, process it now.

      IF mtv$scb.hardware_status [mtc$scb_short_warning_step] > 0 THEN
        IF NOT short_warning_acknowledged THEN
          short_warning_acknowledged := TRUE;
          mtv$idle_step_message := mtv$scb.hardware_status_messages [mtc$scb_short_warning_step].message;
          mtv$scb.hardware_status_messages [mtc$scb_short_warning_step].message_read := FALSE;
          mtp$step_unstep_system (syc$ic_short_power, mtv$idle_step_message);
          short_warning_acknowledged := FALSE;
        IFEND;
      IFEND;

{ Check for the occurrence of a hardware failure.

      IF mtv$scb.hardware_status [mtc$scb_hardware_failure_step] > 0 THEN
        mtv$idle_step_message := mtv$scb.hardware_status_messages [mtc$scb_hardware_failure_step].message;
        mtv$scb.hardware_status_messages [mtc$scb_hardware_failure_step].message_read := FALSE;
        mtp$step_unstep_system (syc$ic_fatal_hardware_error, mtv$idle_step_message);
      IFEND;

{ If the 170 has issued a KILL request, go directly to system step without waiting for idle to complete.
{ Exception: Don't process the STEP_SYSTEM in response to DROPVE from 170 if the system is not sufficiently
{ initialized.  DROPVE on 170 will kill 180 fast enough if the system is truly hung in early deadstart.

      IF mtv$scb.kill_180 THEN
        IF dpv$top_window_p <> NIL THEN
          mtp$step_unstep_system (syc$ic_system_terminated,
                'VEOS8000- 180 terminated by 170 via SCB request');
        IFEND;
      IFEND;

{ If system idle is not already in progress, check for conditions that would initiate a system idle.

      IF (mtv$nosve_control_status.idle_state = mtc$system_not_idle) OR
         (mtv$nosve_control_status.idle_state = mtc$resume_system_in_progress) THEN
        IF mtv$scb.hardware_status [mtc$scb_long_warning_idle] > 0 THEN
          idle_code := syc$ic_long_power;
          mtv$idle_step_message := mtv$scb.hardware_status_messages [mtc$scb_long_warning_idle].message;
          mtv$scb.hardware_status_messages [mtc$scb_long_warning_idle].message_read := FALSE;
        ELSEIF mtv$scb.hardware_status [mtc$scb_hardware_failure_idle] > 0 THEN
          idle_code := syc$ic_hardware_idle;
          mtv$idle_step_message := mtv$scb.hardware_status_messages [mtc$scb_hardware_failure_idle].message;
          mtv$scb.hardware_status_messages [mtc$scb_hardware_failure_idle].message_read := FALSE;
        ELSE
          idle_code := idle_requested;
        IFEND;

        IF idle_code <> syc$ic_null THEN
          mtp$initiate_system_idle (idle_code);
        IFEND;
      IFEND;

{ Check for a STEP request.

      IF mtv$scb.nos_180_status.system_status.step_status_block.requested_status = mtc$stepped_system THEN
        mtp$step_unstep_system (syc$ic_step_command, 'VEOS9100- System STEPPED');

{ The system will return to this point when it is unstepped, so make sure the idle code is cleared.

        idle_requested := syc$ic_null;

      IFEND; { Check for a STEP request. }

      IF mtv$scb.nos_180_status.system_status.idle_status_block.requested_status <> mtc$idled_system THEN
        mtp$monitor_processor_status;
      IFEND;

    ELSE { mtv$nosve_control_status.step_state = mtc$system_stepped }

{ Monitor the status of the STEPPED system.
{ Read the 'live' MCR.

      local_monitor_conditions.int := #read_register (osc$pr_monitor_condition_reg);
      IF NOT (osc$short_warning IN local_monitor_conditions.mask) AND (mtv$scb.hardware_status
            [mtc$scb_short_warning_step] > 0) THEN

{ The system got to this point because it saw a short warning MCR bit set and stepped while the short warning
{ bit cleared asynchronously.  Clear the short warning hardware status bit.

        mtv$scb.hardware_status [mtc$scb_short_warning_step] := 0;
      IFEND;

      hardware_status := mtv$scb.hardware_status;
      IF hardware_status [mtc$scb_short_warning_step] > 0 THEN
        mtv$short_warning_seen := #free_running_clock (0);
      IFEND;
      IF (#free_running_clock (0) - mtv$short_warning_seen) < one_second THEN
        current_hardware_errors := $hardware_errors [mtc$scb_short_warning_step];
      ELSE
        mtv$scb.hardware_status_messages [mtc$scb_short_warning_step].message :=
              '         ERR=D706         SHORT POWER WARNING NORMAL';
        mtv$scb.hardware_status_messages [mtc$scb_short_warning_step].message_read := TRUE;
      IFEND;
      IF hardware_status [mtc$scb_hardware_failure_step] > 0 THEN
        current_hardware_errors := current_hardware_errors + $hardware_errors [mtc$scb_hardware_failure_step];
      IFEND;
      IF hardware_status [mtc$scb_long_warning_idle] > 0 THEN
        current_hardware_errors := current_hardware_errors + $hardware_errors [mtc$scb_long_warning_idle];
      IFEND;
      IF hardware_status [mtc$scb_hardware_failure_idle] > 0 THEN
        current_hardware_errors := current_hardware_errors + $hardware_errors [mtc$scb_hardware_failure_idle];
      IFEND;

{ Check for changes in hardware_status since the last time this procedure was called.

      IF current_hardware_errors <> previous_hardware_errors THEN
        new_hardware_errors := current_hardware_errors - previous_hardware_errors;
        IF new_hardware_errors <> $hardware_errors [ ] THEN
          FOR hardware_index := mtc$scb_short_warning_step TO mtc$scb_hardware_failure_idle DO
            IF hardware_index IN new_hardware_errors THEN
              IF mtv$scb.hardware_status_messages [hardware_index].message_read THEN
                IF NOT mtv$operator_console_hung THEN
                  dpp$display_error (mtv$scb.hardware_status_messages [hardware_index].message);
                IFEND;
                mtv$scb.hardware_status_messages [hardware_index].message_read := FALSE;
              IFEND;
            IFEND;
          FOREND;
        IFEND; { Hardware errors have occurred. }

        cleared_hardware_errors := previous_hardware_errors - current_hardware_errors;
        IF cleared_hardware_errors <> $hardware_errors [ ] THEN
          FOR hardware_index := mtc$scb_short_warning_step TO mtc$scb_hardware_failure_idle DO
            IF hardware_index IN cleared_hardware_errors THEN
              IF mtv$scb.hardware_status_messages [hardware_index].message_read THEN
                dpp$display_error (mtv$scb.hardware_status_messages [hardware_index].message);
                mtv$scb.hardware_status_messages [hardware_index].message_read := FALSE;
              IFEND;
            IFEND;
          FOREND;
        IFEND; { Hardware errors have cleared. }
      IFEND; { Change in hardware status? }

      IF current_hardware_errors = $hardware_errors [ ] THEN

{ All hardware errors are clear.

        IF (previous_hardware_errors <> current_hardware_errors) OR (mtv$check_if_hdw_cleared_before) THEN

{ This is the first time the system has noticed that all hardware errors are clear.

          mtv$check_if_hdw_cleared_before := FALSE;
          IF mtv$scb.nos_180_status.idle_code = syc$ic_short_power THEN
            clear_message := mtv$scb.hardware_status_messages [mtc$scb_short_warning_step].message;
            mtv$scb.hardware_status_messages [mtc$scb_short_warning_step].message_read := FALSE;
            dpp$display_error ('POWER/ENVIRONMENT NORMAL                                               ');
            mtp$display_to_top_line (clear_message);
            mtp$store_informative_message (clear_message);
          ELSEIF mtv$scb.nos_180_status.idle_code = syc$ic_fatal_hardware_error THEN
            clear_message := mtv$scb.hardware_status_messages [mtc$scb_hardware_failure_step].message;
            mtv$scb.hardware_status_messages [mtc$scb_hardware_failure_step].message_read := FALSE;
            dpp$display_error ('HARDWARE ENVIRONMENT NORMAL                                            ');
            mtp$display_to_top_line (clear_message);
            mtp$store_informative_message (clear_message);
          ELSEIF mtv$scb.nos_180_status.idle_code = syc$ic_long_power THEN
            clear_message := mtv$scb.hardware_status_messages [mtc$scb_long_warning_idle].message;
            mtv$scb.hardware_status_messages [mtc$scb_long_warning_idle].message_read := FALSE;
            dpp$display_error ('POWER/ENVIRONMENT NORMAL                                               ');
            mtp$display_to_top_line (clear_message);
            mtp$store_informative_message (clear_message);
          ELSEIF mtv$scb.nos_180_status.idle_code = syc$ic_hardware_idle THEN
            clear_message := mtv$scb.hardware_status_messages [mtc$scb_hardware_failure_idle].message;
            mtv$scb.hardware_status_messages [mtc$scb_hardware_failure_idle].message_read := FALSE;
            dpp$display_error ('HARDWARE ENVIRONMENT NORMAL                                            ');
            mtp$display_to_top_line (clear_message);
            mtp$store_informative_message (clear_message);
          IFEND;

          IF mtv$nosve_control_status.idle_state <> mtc$system_idle THEN

{ If the system is in this state it is due to one of the following:
{        1. a short warning (which cleared)
{        2. a STEP_SYSTEM command
{        3. a software error below the CPU halt_ring or other software breakpoint
{        4. some other reason.
{ If the system stepped for one of the first three reasons the system can UNSTEP.  If it stepped
{ because of a short_warning which cleared we can UNSTEP the system automatically now and
{ continue with whatever the system was doing before it stepped.

            IF (mtv$scb.nos_180_status.idle_code = syc$ic_short_power) AND
               (mtv$automatic_unstep_resume) THEN { automatic unstep }
              mtv$scb.nos_180_status.system_status.step_status_block.requested_status := mtc$unstepped_system;
            ELSEIF (mtv$scb.nos_180_status.idle_code = syc$ic_short_power) OR
                   (mtv$scb.nos_180_status.idle_code = syc$ic_step_command) OR
                   (mtv$scb.nos_180_status.idle_code = syc$ic_disk_error) OR
                   (mtv$scb.nos_180_status.idle_code = syc$ic_software_breakpoint) THEN { manual unstep }
              dpp$display_error ('System ready to UNSTEP manually; use console command.');
            IFEND; { determine automatic/manual control }

          ELSE { system is idle }

{ If the system is in this state the system is idle because of
{        1. a long_warning (which cleared)
{        2. an IDLE_SYSTEM command
{        3. a hardware_idle condition (which does not currently clear)
{        4. we were caught resuming when a STEP condition occurred
{ If the system idled for one of the first two reasons the system can RESUME.  If it idled because of a
{ long_warning (which cleared) the system can RESUME automatically and allow jobs to continue processing.
{ If the system was resuming when a STEP condition occurred and this is an unsteppable condition
{ then automatically UNSTEP the system (if automatic_unstep_resume is allowed) or allow the
{ UNSTEP_SYSTEM command to be invoked.

            IF (mtv$scb.nos_180_status.idle_code = syc$ic_long_power) AND
                  (mtv$automatic_unstep_resume) THEN { automatic resume }
              mtv$scb.nos_180_status.system_status.idle_status_block.requested_status := mtc$running_system;
              mtv$scb.nos_180_status.system_status.step_status_block.requested_status :=
                    mtc$unstepped_system;
            ELSEIF (mtv$scb.nos_180_status.idle_code = syc$ic_long_power) OR
                   (mtv$scb.nos_180_status.idle_code = syc$ic_idle_command)  THEN   { manual resume }
              dpp$display_error ('System ready to RESUME manually; use console command.');
            ELSEIF (mtv$scb.nos_180_status.idle_code = syc$ic_short_power) AND
               (mtv$automatic_unstep_resume) THEN { automatic unstep }
              mtv$scb.nos_180_status.system_status.step_status_block.requested_status := mtc$unstepped_system;
            ELSEIF (mtv$scb.nos_180_status.idle_code = syc$ic_short_power) OR
                   (mtv$scb.nos_180_status.idle_code = syc$ic_step_command) OR
                   (mtv$scb.nos_180_status.idle_code = syc$ic_disk_error) OR
                   (mtv$scb.nos_180_status.idle_code = syc$ic_software_breakpoint) THEN { manual unstep }
              dpp$display_error ('System ready to UNSTEP manually; use console command.');
            IFEND; { determine automatic/manual control }

          IFEND; { determine idle/non-idle status of the system }
        IFEND; { first time to notice lack of hardware errors }
      IFEND; { hardware errors have cleared }

      previous_hardware_errors := current_hardware_errors;
      current_hardware_errors := $hardware_errors [ ];
    IFEND;

{ Determine the status of 170 and log a message describing any abnormal conditions like termination.

    IF mtv$scb.hardware_status [mtc$scb_170_status] > 0 THEN
      IF mtv$scb.hardware_status_messages [mtc$scb_170_status].message_read THEN
        nos_170_message := mtv$scb.hardware_status_messages [mtc$scb_170_status].message;
        mtv$scb.hardware_status_messages [mtc$scb_170_status].message_read := FALSE;
        mtp$store_informative_message (nos_170_message);
        dpp$display_error (nos_170_message);
      IFEND;
    IFEND;

  PROCEND mtp$monitor_system_status;
?? TITLE := 'MTP$DISPLAY_TO_TOP_LINE', EJECT ??

{ PURPOSE:
{   This procedure accepts a string and displays it to the top line of the system console.  It also stores
{   the string in the System Deadstart Status data in the SSR.  This information is later logged in a
{   statistic (if the information survives the possible crash).

  PROCEDURE [XDCL] mtp$display_to_top_line
    (    message: string ( * ));

    mtv$idle_message_line.text_size := top_line_msg_size;
    mtv$idle_message_line.text := message ;
    mtv$idle_message_line.next_line_rma := mtv$idle_message_line.next_line_rma + 1;

    dsp$mtr_save_top_line_message (message);

  PROCEND mtp$display_to_top_line;
?? TITLE := 'MTP$INITIATE_SYSTEM_IDLE', EJECT ??
{------------------------------------------------------------------------------------------
{ This procedure is called to initiate a system idle. A signal is sent to
{ the job monitor of the system job to ready itself upon return to job_mode.
{ In case the need arises, the cause of the idle is saved in a local static
{ variable, and the next time a call is made to mtp$monitor_system_status,
{ an attempt is made again to send the signal.
{------------------------------------------------------------------------------------------

  PROCEDURE [XDCL] mtp$initiate_system_idle (idle_code: syt$180_idle_code);

    VAR
      status: syt$monitor_status;

    IF NOT mtv$sys_core_init_complete THEN
      idle_requested := idle_code;
      RETURN;
    IFEND;

    mtv$scb.nos_180_status.system_status.idle_status_block.requested_status := mtc$idled_system;
    tmp$monitor_ready_system_task (tmc$stid_job_monitor, status);
    IF NOT status.normal THEN
      mtp$error_stop ('MT - cannot ready job_monitor task in mtp$initiate_system_idle');
    ELSE
      mtv$scb.nos_180_status.cause_of_idle := idle_code;
    IFEND;

  PROCEND mtp$initiate_system_idle;
?? TITLE := 'MTP$PROCESS_SHORT_WARNING', EJECT ??
{------------------------------------------------------------------------------------------
{ This procedure is called to process a short warning condition. The processing
{ currently done is to idle 180 as quickly as possible (by invoking the STEP/UNSTEP
{ procedure MTP$STEP_UNSTEP_SYSTEM) and hope the image file write is fast enough.
{------------------------------------------------------------------------------------------

  PROCEDURE [XDCL] mtp$process_short_warning;

    TYPE
      monitor_mask = RECORD
        CASE boolean OF
        = FALSE =
          mask: ost$monitor_conditions,
        = TRUE =
          int: 0 .. 0ffff(16),
        CASEND,
      RECEND;

    VAR
      local_monitor_conditions: monitor_mask,
      local_monitor_mask: monitor_mask;

{ Read the 'live' MCR.

    local_monitor_conditions.int := #read_register (osc$pr_monitor_condition_reg);
    IF NOT (osc$short_warning IN local_monitor_conditions.mask) THEN
      { The system got to this point because there were some 'stale' short_warning bits left set somewhere.
      RETURN;
    IFEND;

{ Clear the short_warning bit mask bit (if it is set).

    local_monitor_mask.int := #read_register (osc$pr_monitor_mask_reg);
    IF osc$short_warning IN local_monitor_mask.mask THEN
      local_monitor_mask.mask := local_monitor_mask.mask - $ost$monitor_conditions [osc$short_warning];
      #write_register (osc$pr_monitor_mask_reg, local_monitor_mask.int);
    IFEND;

    mtv$short_warning_seen := #free_running_clock (0);
    IF mtv$scb.nos_180_status.system_status.step_status_block.actual_status = mtc$unstepped_system THEN
{
{ The system was running normally, or it was caught stepping without actually processing
{ the original reason for the STEP.  It needs to STEP as quickly as possible.
{
      mtv$scb.hardware_status [mtc$scb_short_warning_step] := 1;
      mtv$scb.hardware_status_messages [mtc$scb_short_warning_step].message :=
            '         ERR=D703         SHORT POWER WARNING';
      mtv$scb.hardware_status_messages [mtc$scb_short_warning_step].message_read := TRUE;
      mtp$step_unstep_system (syc$ic_short_power, '         ERR=D703         SHORT POWER WARNING');    {VEOS}

    ELSEIF mtv$scb.nos_180_status.system_status.step_status_block.requested_status =
          mtc$unstepped_system THEN

{ The system was unstepping, or it was resuming.  The system can wait until it cleans up the
{ previous IDLE/STEP sequence before processing this call to STEP.

        dpp$display_error ('Short power warning detected in MCR');
    ELSE

{ The system was stepped.  The system will note the short_warning condition in the procedure
{ MTP$MONITOR_SYSTEM_STATUS.  Therefore, do nothing here.

    IFEND;

  PROCEND mtp$process_short_warning;

?? TITLE := 'PROCEDURE mtp$mtr_error_stop', EJECT ??

{
{  This procedure is called by the monitor interrupt handler when a fatal MCR/UCR error in monitor
{  is found by the trap handler.  The procedure examines the stack frame that was trapped and extracts
{  the P, MCR, UCR registers and displays their contents within the error message.
{
{   Parameters:  NONE
{   XREF DECK:   NONE - procedure only called from monitor_interrupt_handler which is not CYBIL code
{

  PROCEDURE [XDCL] mtp$mtr_error_stop;

    VAR
      line: string (top_line_msg_size),
      p: ^ost$stack_frame_save_area,
      i: 0 .. 0ffff(16),
      pos: 0 .. 76,
      scr: integer;

    VAR
      hex_char: [STATIC] array [0 .. 15] of string (1) := ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            'A', 'B', 'C', 'D', 'E', 'F'];

    line := 'ERR=VEOS1010- MONITOR MCR/UCR: PVA= ';
    pos := 37;
    p := #PREVIOUS_SAVE_AREA ();
    p := p^.minimum_save_area.a2_previous_save_area;
    line (pos, 1) := hex_char [p^.minimum_save_area.p_register.pva.ring];
    pos := pos + 2;
    scr := p^.minimum_save_area.p_register.pva.seg;
    FOR i := pos + 2 DOWNTO pos DO
      line (i, 1) := hex_char [scr MOD 16];
      scr := scr DIV 16;
    FOREND;
    pos := pos + 4;
    scr := p^.minimum_save_area.p_register.pva.offset;
    FOR i := pos + 7 DOWNTO pos DO
      line (i, 1) := hex_char [scr MOD 16];
      scr := scr DIV 16;
    FOREND;
    pos := pos + 10;
    #UNCHECKED_CONVERSION (p^.user_condition_register, i);
    scr := i;
    line (pos, 6) := '  UCR=';
    pos := pos + 6;
    FOR i := pos + 3 DOWNTO pos DO
      line (i, 1) := hex_char [scr MOD 16];
      scr := scr DIV 16;
    FOREND;
    pos := pos + 4;
    #UNCHECKED_CONVERSION (p^.monitor_condition_register, i);
    scr := i;
    line (pos, 6) := '  MCR=';
    pos := pos + 6;
    FOR i := pos + 3 DOWNTO pos DO
      line (i, 1) := hex_char [scr MOD 16];
      scr := scr DIV 16;
    FOREND;

    mtp$step_unstep_system (syc$ic_fatal_software_error, line);

  PROCEND mtp$mtr_error_stop;

?? TITLE := 'PROCEDURE mtp$mtr_step_unstep_system', EJECT ??
{------------------------------------------------------------------------------------------
{ This procedure is the entry point which is used by RQPROC in
{ MTM$MONITOR_INTERRUPT_HANDLER.  It is called via a request block which is set up in
{ the job_mode procedure OSP$IDLE_RESUME_SYSTEM_JOB and is then passed to monitor_mode.
{------------------------------------------------------------------------------------------

  PROCEDURE [XDCL] mtp$mtr_step_unstep_system (rb: mmt$rb_idle_system);

    mtp$step_unstep_system (rb.idle_code, rb.error_message);

  PROCEND mtp$mtr_step_unstep_system;

?? TITLE := 'PROCEDURE mtp$step_unstep_system', EJECT ??

  PROCEDURE [XDCL] mtp$step_unstep_system
        (idle_code: syt$180_idle_code;
         text: string (*<=top_line_msg_size) );

    CONST
      should_not_return = 'mtp$idle_180 (false) RETURNED' ;

    TYPE
      message = string (71);

    VAR
      messages: [STATIC, READ] ARRAY [syt$180_idle_code] OF message := [
{    syc$ic_null                    } 'NULL',
{    syc$ic_system_terminated       } 'VEOS0002- System TERMINATED via OPERATOR COMMAND',

{    The next two codes are ABORT codes}

{    syc$ic_fatal_hardware_error    } 'VEOS0003- System ABORTED due to HARDWARE FAILURE',
{    syc$ic_fatal_software_error    } 'VEOS0004- System ABORTED due to SOFTWARE FAILURE',

{    The following codes are IDLE codes}

{    syc$ic_long_power              } 'VEOS0005- System IDLED due to LONG POWER WARNING',
{    syc$ic_hardware_idle           } 'VEOS0006- System IDLED due to HARDWARE IDLE CONDITION',
{    syc$ic_idle_command            } 'VEOS0007- System IDLED via OPERATOR COMMAND',

{    The following codes are STEP codes}

{    syc$ic_step_command            } 'VEOS0008- System STEPPED via OPERATOR COMMAND',
{    syc$ic_short_power             } 'VEOS0009- System STEPPED due to SHORT POWER WARNING',
{    syc$ic_disk_error              } 'VEOS0010- System STEPPED due to DISK ERROR',
{    syc$ic_software_breakpoint     } 'VEOS0011- System STEPPED due to SOFTWARE SELECTED BREAKPOINT'];


    TYPE
      monitor_mask = RECORD
        CASE boolean OF
        = FALSE =
          mask: ost$monitor_conditions,
        = TRUE =
          int: 0 .. 0ffff(16),
        CASEND,
      RECEND;

    VAR
      local_monitor_mask: monitor_mask;

    VAR
      first_step_code: [STATIC] syt$180_idle_code,
      first_step_text: [STATIC] string (top_line_msg_size),
      first_step_text_size: [STATIC] 0..top_line_msg_size,
      command_needed_to_unstep_resume: [STATIC] boolean,
      mtv$trace_p: [XDCL] ARRAY [0 .. osc$max_number_of_processors - 1] OF  ^cell,
      mtv$step_lock_0: [XDCL] mtt$monitor_lock := 0,
      mtv$step_lock_1: [XDCL] mtt$monitor_lock := 0,
      mtv$step_lock_2: [XDCL] mtt$monitor_lock := 0,
      mtv$unstep_lock_0: [XDCL] mtt$monitor_lock := 0,
      mtv$unstep_lock_1: [XDCL] mtt$monitor_lock := 0,
      mtv$unstep_lock_2: [XDCL] mtt$monitor_lock := 0,
      locked: boolean,
      osv$step_kbp: [XDCL] ARRAY [0 .. osc$max_number_of_processors - 1]
        OF integer := [REP osc$max_number_of_processors of 0],
      current_cst_p: ^ost$cpu_state_table,
      i,
      additional_cpus_not_stepped,
      additional_cpus_not_running: 0 .. 0ff(16),
      old_te: 0 .. 3,
      idle_te: 0 .. 3,
      keypoints_enabled,
      endtime: integer,
      status: syt$monitor_status;

{ Leave the following 3 statements FIRST - in case of idle while keypoints are active.
{ Thank You.

{ Terminate keypoint collection.

    keypoints_enabled := #read_register (osc$pr_clear_keypoint_enable);
    osv$step_kbp [#read_register (osc$pr_processor_id)] := #read_register (osc$pr_keypoint_buffer_ptr);
    #write_register (osc$pr_clear_keypoint_enable, osc$pr_clear_keypoint_enable);

{ Begin the NOTIFY phase of step processing.  The first CPU to reach this point
{ will send interrupts to the other processors to indicate that they should step also.

    i#mtr_disable_traps (old_te);

{ Set the variable to display a flashing message on the 170 console.

    dpv$180_operator_action := TRUE;

{ Deselect the short_warning trap bit.

    local_monitor_mask.int := #read_register (osc$pr_monitor_mask_reg);
    local_monitor_mask.mask := local_monitor_mask.mask - $ost$monitor_conditions [osc$short_warning];
    #write_register (osc$pr_monitor_mask_reg, local_monitor_mask.int);

    IF mtv$ns_xp_p <> NIL THEN
      mtv$ns_xp_p^.monitor_mask := mtv$ns_xp_p^.monitor_mask - $ost$monitor_conditions [osc$short_warning];
    IFEND;

    current_cst_p := mtf$cst_p ();

{ If the SCB does not reflect a request for the system to step via the operator commands
{ IDLE_SYSTEM, STEP_SYSTEM, and TERMINATE_SYSTEM, the system is stepping/idling due to
{ system conditions like short_warning, DUEs, monitor_errors, etc.  Make sure the SCB has
{ a step request of 'system_stepped' so the system does not immediately return from the
{ idle loop in MTP$IDLE_180.

    mtv$scb.nos_180_status.system_status.step_status_block.requested_status := mtc$stepped_system;

{ Signal the other processor(s) to begin stepping.

    REPEAT
      mtp$set_lock (mtv$step_lock_0, locked);
    UNTIL locked;
    IF mtv$nosve_control_status.term_or_restart_section = mtc$trs_system_ready_to_step THEN
      mtv$nosve_control_status.step_state := mtc$system_is_stepping;
      mtv$nosve_control_status.term_or_restart_section := mtc$trs_step_processors;
      FOR i := 0 TO osv$cpus_physically_configured - 1 DO
        IF (i <> current_cst_p^.cst_index) AND (mtv$cst0 [i].cpu_state.next_state = osc$cpu_running) THEN
          mtv$trace_p [i] := mtv$cst0 [i].trace_control.buffer_p;
          mtv$cst0 [i].idle_code := idle_code;
          mtp$step_processor (i);
        IFEND;
      FOREND;
      mtv$trace_p [current_cst_p^.cst_index] := current_cst_p^.trace_control.buffer_p;
      current_cst_p^.trace_control.buffer_p := ^mtv$dummy_trace_buffer;
      current_cst_p^.cpu_state.next_state := osc$cpu_stepped;
      first_step_code := idle_code;
      first_step_text := text;
      first_step_text_size := top_line_msg_size;
      IF text(1,4) <> 'ERR=' THEN
        IF (idle_code = syc$ic_fatal_software_error) OR (idle_code = syc$ic_software_breakpoint) THEN
          first_step_text := 'ERR=';  {if appropriate, enforce convention that msg begin with "ERR="}
          first_step_text(5,*) := text;
        IFEND;
      IFEND;
    IFEND;
    mtp$clear_lock (mtv$step_lock_0);

{ End the NOTIFY phase of step processing. Shift the cpu_state.next_state
{ to the cpu_state.current_state.

    IF (current_cst_p^.cpu_state.next_state = osc$cpu_stepped) AND
         (current_cst_p^.processor_state = cmc$on) THEN
      current_cst_p^.ext_int_request.step_processor := FALSE;
      current_cst_p^.cpu_state.current_state := osc$cpu_stepped;
    IFEND;

{ Wait for the other processors to step; they will reach the call to
{ MTP$SET_LOCK below and wait.

    REPEAT
      mtp$set_lock (mtv$step_lock_1, locked);
    UNTIL locked;
    IF mtv$nosve_control_status.term_or_restart_section = mtc$trs_step_processors THEN
      mtv$nosve_control_status.term_or_restart_section := mtc$trs_idle_processors;
      endtime := #free_running_clock (0) + 2000000;
      IF first_step_code <> syc$ic_short_power THEN
        REPEAT
          additional_cpus_not_stepped := 0;
          FOR i := 0 TO osv$cpus_physically_configured - 1 DO
            IF (i <> current_cst_p^.cst_index) AND (mtv$cst0 [i].cpu_state.
                  current_state = osc$cpu_running) THEN
              additional_cpus_not_stepped := additional_cpus_not_stepped + 1;
            IFEND;
          FOREND;
        UNTIL (additional_cpus_not_stepped = 0) OR (#free_running_clock (0) > endtime);
      IFEND;

{ Idle down the peripheral processors used for IO here.  Don't wait for the idle if this is a short_warning.

      IF first_step_code <> syc$ic_short_power THEN
        iop$idle_all_paths (TRUE, status);
      ELSE
        iop$idle_all_paths (FALSE, status);
      IFEND;

    IFEND;
    mtp$clear_lock (mtv$step_lock_1);

{ At this point either 1). all of the CPUs are stepped, or 2). time has run out.
{ At this point the system is stepped and we should be able to come out of this
{ state with an 'UNSTEP' command or a 'RESUME' command (in the case of IDLE_SYSTEM).

    REPEAT
      mtp$set_lock (mtv$step_lock_2, locked);
    UNTIL locked;
    IF mtv$nosve_control_status.term_or_restart_section = mtc$trs_idle_processors THEN
      mtv$nosve_control_status.term_or_restart_section := mtc$trs_stepped_system;

{ It might be nice to see why we stepped.

      mtp$display_to_top_line(first_step_text) ;  {display text that was input to mtp$step_unstep_system}
      IF NOT mtv$operator_console_hung THEN
        dpp$display_error (messages [first_step_code]); {display message corresponding to step_code}
      IFEND;
      mtp$store_informative_statistic (osc$step_statistic, first_step_code);
      mtv$scb.nos_180_status.idle_code := first_step_code;

      CASE first_step_code OF
{ In some cases we do not need a dump during termination.
      = syc$ic_system_terminated =
        IF NOT mtv$scb.kill_180 THEN    {if it was not a DROP VE command, advance ds seq}
          dsp$advance_ds_sequence_in_mtr (dsc$dss_system_terminated);
        IFEND;
      ELSE
{ In other cases, we do.
        ;
      CASEND;
      mtv$scb.nos_180_status.system_status.step_status_block.actual_status := mtc$stepped_system;
      mtv$nosve_control_status.step_state := mtc$system_stepped;
      mtv$nosve_control_status.term_or_restart_section := mtc$trs_system_ready_to_unstep;
      mtv$check_if_hdw_cleared_before := TRUE;
    IFEND;
    mtp$clear_lock (mtv$step_lock_2);

    current_cst_p^.idle_code := idle_code;
    IF (first_step_code = syc$ic_system_terminated) THEN
      dsp$mtr_change_bct_flag (dsc$rb_sds_set_bct_flag, dsc$rb_sds_bct_ts_by_operator);

{ For a TERMINATE_SYSTEM, the OS type must be accounted for.
{ Avoid halting the processor in standalone mode.  The mtp$idle_180
{ call must be used in standalone, lest the processor(s) halt.  mtp$terminate_
{ 180 will RETURN in standalone mode, since there is not a 170 side to
{ run.  ( This is OK in a standalone recovery_complete )

      IF ( osv$170_os_type = osc$ot7_none ) AND  { Standalone T.S.
         ( first_step_code = syc$ic_system_terminated ) THEN
        mtp$idle_180 ( FALSE );                  { NO RETURN FROM HERE.
        mtp$error_stop ( should_not_return );    { Error if return.
      ELSE
        mtp$terminate_180;
        i#program_error;                         { HALTS PROCESSOR
      IFEND;

    ELSEIF (first_step_code = syc$ic_idle_command) OR
        (first_step_code = syc$ic_step_command) OR
        (first_step_code = syc$ic_disk_error) OR
        (first_step_code = syc$ic_software_breakpoint) OR
{!      (first_step_code = syc$ic_hardware_idle) OR} { Code deactivated until we REALLY can do this.
        (first_step_code = syc$ic_long_power)  OR
        (first_step_code = syc$ic_short_power) THEN
{ If we have a short_warning which goes away (i.e. the system stays up) the operator can unstep the system.
      mmp$include_p_reg_in_dump;
      i#mtr_enable_traps (idle_te);
      IF first_step_code = syc$ic_idle_command THEN
        dsp$mtr_change_bct_flag (dsc$rb_sds_set_bct_flag, dsc$rb_sds_bct_sys_has_idled);
      IFEND;
      mtp$idle_180 (TRUE);                                   { TRUE = (system is resumable) }
      dsp$mtr_change_bct_flag (dsc$rb_sds_clear_bct_flag, dsc$rb_sds_bct_sys_has_idled);
      i#mtr_restore_traps (idle_te);
    ELSE { anything else }
      mmp$include_p_reg_in_dump;
      IF osv$170_os_type = osc$ot7_none THEN { Standalone mode; allow Express Deadstart Dump }
        mtp$idle_180 (FALSE);                                { FALSE = (system is not resumable) }
        i#program_error;
      ELSE { NOT Standalone mode; allow K.*RUN dump }
        mtp$terminate_180;
        i#program_error;
      IFEND;
    IFEND;

{ This code furthers the process of UNSTEP_SYSTEM once the system has been trapped
{ or exchanged (exchange goes away in standalone) into becoming active again.

    mtv$nosve_control_status.step_state := mtc$system_is_unstepping;
    IF current_cst_p^.processor_state = cmc$on THEN
      current_cst_p^.cpu_state.current_state := osc$cpu_running;
      current_cst_p^.cpu_state.next_state := osc$cpu_running;
      current_cst_p^.idle_code := syc$ic_null;
      current_cst_p^.cpu_alive_flag := #free_running_clock (0);
    IFEND;

{ Wait for the other processors to unstep; they will reach the call to MTP$SET_LOCK below and wait.

    REPEAT
      mtp$set_lock (mtv$unstep_lock_0, locked);
    UNTIL locked;
    IF mtv$nosve_control_status.term_or_restart_section = mtc$trs_system_ready_to_unstep THEN
      mtv$nosve_control_status.term_or_restart_section := mtc$trs_unstep_processors;
      mtv$step_condition_has_occurred := TRUE;
      IF first_step_code = syc$ic_software_breakpoint  THEN

{ Display again the top line message but this time display it to the Critical Window

        dpp$display_error ('Unstepping from the following Software Breakpoint:');
        dpp$display_error (first_step_text(1,71) );
      IFEND;
      first_step_text := ' ';
      first_step_text_size := top_line_msg_size;
      command_needed_to_unstep_resume := ((first_step_code = syc$ic_idle_command) OR
            (first_step_code = syc$ic_step_command) OR (first_step_code = syc$ic_disk_error) OR
            (first_step_code = syc$ic_software_breakpoint));
      first_step_code := syc$ic_null;

{ Resume the peripheral processors used for IO here.

      iop$resume_all_paths (status);

    IFEND;
    mtp$clear_lock (mtv$unstep_lock_0);

    REPEAT
      mtp$set_lock (mtv$unstep_lock_1, locked);
    UNTIL locked;
    IF mtv$nosve_control_status.term_or_restart_section = mtc$trs_unstep_processors THEN
      endtime := #free_running_clock (0) + 2000000;
      REPEAT
        additional_cpus_not_running := 0;
        FOR i := 0 TO osv$cpus_physically_configured - 1 DO
          IF (i <> current_cst_p^.cst_index) AND (mtv$cst0 [i].cpu_state.
                current_state = osc$cpu_stepped) THEN
            additional_cpus_not_running := additional_cpus_not_running + 1;
          IFEND;
        FOREND;
      UNTIL (additional_cpus_not_running = 0) OR (#free_running_clock (0) > endtime);
      mtv$nosve_control_status.term_or_restart_section := mtc$trs_clean_up_prev_term;
    IFEND;
    mtp$clear_lock (mtv$unstep_lock_1);

{ At this point either 1). all of the CPUs are restarted, or 2). time has run out.

    REPEAT
      mtp$set_lock (mtv$unstep_lock_2, locked);
    UNTIL locked;
    IF mtv$nosve_control_status.term_or_restart_section = mtc$trs_clean_up_prev_term THEN
      mtv$nosve_control_status.term_or_restart_section := mtc$trs_system_ready_to_step;

{ Clear the Top Line message by writing a line of blanks}

      mtp$display_to_top_line (first_step_text);
      IF mtv$scb.nos_180_status.idle_code = mtv$scb.nos_180_status.cause_of_idle THEN
        mtv$scb.nos_180_status.cause_of_idle := first_step_code;
      IFEND;
      mtv$scb.nos_180_status.idle_code := first_step_code;
      IF mtv$scb.nos_180_status.system_status.idle_status_block.actual_status = mtc$idled_system THEN
        IF (NOT mtv$automatic_unstep_resume) OR (command_needed_to_unstep_resume) THEN
          dpp$display_error ('System resuming via console command');
        ELSE {automatic resume}
          dpp$display_error ('System resuming automatically;' CAT
                ' Condition for idle has been cleared');
        IFEND;
      ELSE
        IF (NOT mtv$automatic_unstep_resume) OR (command_needed_to_unstep_resume) THEN
          dpp$display_error ('System unstepped via console command');
        ELSE {automatic resume}
          dpp$display_error ('System unstepped automatically;' CAT
                ' Condition for STEP has been cleared');
        IFEND;
      IFEND;
      mtp$store_informative_statistic (osc$unstep_statistic, first_step_code);
      FOR i := 0 TO osv$cpus_physically_configured - 1 DO
        IF mtv$cst0 [i].processor_state = cmc$on THEN
          mtv$cst0 [i].trace_control.buffer_p := mtv$trace_p [i];
        IFEND;
      FOREND;
    IFEND;
    mtp$clear_lock (mtv$unstep_lock_2);

    IF current_cst_p^.processor_state = cmc$on THEN
      IF keypoints_enabled = 1 THEN
        #write_register (osc$pr_set_keypoint_enable, osc$pr_set_keypoint_enable);
      IFEND;
    IFEND;

    mtv$nosve_control_status.step_state := mtc$system_not_stepped;
    mtv$scb.nos_180_status.system_status.step_status_block.actual_status := mtc$unstepped_system;

{ Reselect the short_warning trap bit.

    local_monitor_mask.int := #read_register (osc$pr_monitor_mask_reg);
    local_monitor_mask.mask := local_monitor_mask.mask + $ost$monitor_conditions [osc$short_warning];
    #write_register (osc$pr_monitor_mask_reg, local_monitor_mask.int);

    IF mtv$ns_xp_p <> NIL THEN
      mtv$ns_xp_p^.monitor_mask := mtv$ns_xp_p^.monitor_mask + $ost$monitor_conditions [osc$short_warning];
    IFEND;

    cmp$enable_all_connections;

    dpv$180_operator_action := FALSE;
    i#mtr_restore_traps (old_te);

  PROCEND mtp$step_unstep_system;

?? TITLE := 'PROCEDURE mtp$step_processor', EJECT ??

  PROCEDURE mtp$step_processor (lpid: ost$logical_processor_id);

    IF mtv$cst0 [lpid].cpu_state.current_state = osc$cpu_stepped THEN
      {return an error- processor already stepped
    ELSE
      mtv$cst0 [lpid].cpu_state.next_state := osc$cpu_stepped;
      IF mtv$cst0 [lpid].processor_state = cmc$on THEN
        mtv$cst0 [lpid].ext_int_request.step_processor := TRUE;
        mtp$interrupt_processor (mtv$cst0 [lpid].memory_port_mask);
        mtv$cst0 [lpid].trace_control.buffer_p := ^mtv$dummy_trace_buffer;
      IFEND;
    IFEND;

  PROCEND mtp$step_processor;

?? TITLE := 'mtp$store_informative_statistic', EJECT??

{ PURPOSE:
{   The following procedure stores a system message in the mainframe_wired
{   segment from where it will be picked up and displayed into the system
{   engineering log by the system job_monitor task when it returns to job_mode.

  PROCEDURE mtp$store_informative_statistic
    (    statistic: ost$terminate_continue_stats;
         idle_code: syt$180_idle_code);

    VAR
      message_data_p: ^SEQ(*),
      message_recorded: boolean,
      message_type: dst$system_logging_types,
      terminate_continue_message: ost$terminate_continue_record;

    mtp$get_date_time_at_timestamp (#FREE_RUNNING_CLOCK (0), terminate_continue_message.date_time);
    terminate_continue_message.log_reason := idle_code;
    terminate_continue_message.log_statistic := statistic;

{ Process the message corresponding to the reason for the call to this procedure.
{ Only the processes of 'stepping' and 'unstepping' will ever call this procedure.

    CASE statistic OF
    = osc$step_statistic =
      message_type := dsc$system_termination;
      terminate_continue_message.log_message.value := 'SYSTEM STEP';
      terminate_continue_message.log_message.size := 11;
      CASE idle_code OF
      = syc$ic_fatal_hardware_error, syc$ic_long_power, syc$ic_hardware_idle, syc$ic_short_power =
        dsp$mtr_save_cause_and_time (#FREE_RUNNING_CLOCK (0), dsc$ssr_sds_cause_mainframe);
      = syc$ic_disk_error =
        dsp$mtr_save_cause_and_time (#FREE_RUNNING_CLOCK (0), dsc$ssr_sds_cause_disk);
      = syc$ic_fatal_software_error, syc$ic_software_breakpoint =
        dsp$mtr_save_cause_and_time (#FREE_RUNNING_CLOCK (0), dsc$ssr_sds_cause_software);
      = syc$ic_system_terminated, syc$ic_idle_command, syc$ic_step_command =
        dsp$mtr_save_cause_and_time (#FREE_RUNNING_CLOCK (0), dsc$ssr_sds_cause_operator);
      ELSE
        dsp$mtr_save_cause_and_time (#FREE_RUNNING_CLOCK (0), dsc$ssr_sds_cause_indeterminate);
      CASEND;
    = osc$unstep_statistic =
      message_type := dsc$system_continuation;
      terminate_continue_message.log_message.value := 'SYSTEM UNSTEP';
      terminate_continue_message.log_message.size := 13;
      dsp$mtr_save_cause_and_time (0, dsc$ssr_sds_cause_operator);
    ELSE
      ;
    CASEND;

    message_data_p := #SEQ (terminate_continue_message);
    dsp$report_system_message (message_data_p, message_type, dsc$informative_message, message_recorded);

  PROCEND mtp$store_informative_statistic;

*copyc cml$system_informative_message
?? TITLE := 'PROCEDURE mtp$store_informative_message', EJECT??

{
{ The following procedure stores an informative system message in the
{ mainframe_wired segment from where it will be picked up and displayed
{ into the system engineering log by the system job_monitor task when
{ it returns to job_mode.
{

  PROCEDURE [XDCL] mtp$store_informative_message (message_to_record: string (*));
*copyc ost$informative_message_record

    VAR
      message_data: ^SEQ(*),
      message_recorded: boolean,
      message_record: ost$informative_message_record;

    message_recorded := FALSE;

    message_record.message_type := cml$system_informative_message;

    IF STRLENGTH (message_to_record) <= osc$max_string_size THEN
      message_record.message := message_to_record;
    ELSE
      message_record.message := message_to_record (1, osc$max_string_size);
    IFEND;

    message_data := #SEQ (message_record);
    dsp$report_system_message (message_data, dsc$general_system_message, dsc$informative_message,
          message_recorded);

  PROCEND mtp$store_informative_message;

?? TITLE := 'PROCEDURE mtp$error_stop', EJECT ??

  PROCEDURE [XDCL] mtp$error_stop (text: string (*<=top_line_msg_size-13) );

    VAR enhanced_text: string (top_line_msg_size);

    enhanced_text ( 1,13) := 'ERR=VEOS1000-';
    enhanced_text (14, *) := text;
    mtp$step_unstep_system (syc$ic_fatal_software_error, enhanced_text);

  PROCEND mtp$error_stop;

?? TITLE := 'PROCEDURE mtp$breakpoint_step', EJECT ??

  PROCEDURE [XDCL] mtp$breakpoint_step (text: string (*<=top_line_msg_size-9));

    VAR  breakpoint_msg: string (top_line_msg_size);

    breakpoint_msg(1,9) := 'VEOS9999-';
    breakpoint_msg(10,*) := text;
    mtp$step_unstep_system (syc$ic_software_breakpoint, breakpoint_msg);

  PROCEND mtp$breakpoint_step;

?? TITLE := 'PROCEDURE cyp$error', EJECT ??

  PROCEDURE [XDCL] cyp$error (ec: integer;
        line: integer;
        module_p: ^string (31));

    VAR
      s: string (63),
      k: integer;

    s := 'CYBIL ERROR xx AT LINE xxxxx OF mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm';
    s (14) := CHR ((ec MOD 10) + ORD ('0'));
    s (13) := CHR ((ec DIV 10) + ORD ('0'));
    s (28) := CHR ((line MOD 10) + ORD ('0'));
    s (27) := CHR (((line MOD 100) DIV 10) + ORD ('0'));
    s (26) := CHR (((line MOD 1000) DIV 100) + ORD ('0'));
    s (25) := CHR (((line MOD 10000) DIV 1000) + ORD ('0'));
    s (24) := CHR ((line DIV 10000) + ORD ('0'));
    s (33, 31) := module_p^;
    mtp$error_stop (s);

  PROCEND cyp$error;
?? TITLE := 'mtp$get_date_time_at_timestamp', EJECT ??

{ PURPOSE:
{   This procedure uses a timestamp obtained during system execution and returns a record containing the
{   compact date and time corresponding to that timestamp.

  PROCEDURE [XDCL] mtp$get_date_time_at_timestamp
    (    timestamp: ost$free_running_clock;
     VAR date_time: ost$date_time);

    CONST
      c$max_years = 2155,  {1980 + 255
      c$ms_per_day = 24 * 60 * 60 * 1000;

    VAR
      base_system_time: ost$base_system_time,
      day: integer,
      days_in_the_month_p: ^ARRAY [1 .. 12] OF 1 .. 31,
      elapsed_time: integer,
      hour: -1 .. 47,
      minute: -30 .. 119,
      month: 1 .. 13,
      second: 0 .. 119,
      year: 0 .. c$max_years;

    base_system_time := osv$base_system_time;
    date_time.millisecond := 0;
    date_time.second := 0;
    date_time.minute := 0;
    date_time.hour := 0;
    date_time.day := 1;
    date_time.month := 1;
    date_time.year := 0;

    elapsed_time {ms} := (timestamp {us} - base_system_time.corresponding_microsecond_clock {us}) DIV 1000
          {us/ms};

    elapsed_time := elapsed_time + (base_system_time.hour * 3600 + base_system_time.minute *
          60 + base_system_time.second) * 1000;

    day := base_system_time.day + (elapsed_time DIV c$ms_per_day);
    elapsed_time := elapsed_time MOD c$ms_per_day;
    IF elapsed_time < 0 THEN
      elapsed_time := elapsed_time + c$ms_per_day;
      day := day - 1;
    IFEND;

    date_time.millisecond := elapsed_time {ms} MOD 1000 {ms} ;

    elapsed_time {sec} := elapsed_time {ms} DIV 1000 {ms/sec} ;
    date_time.second := elapsed_time {sec} MOD 60 {sec} ;

    elapsed_time {min} := elapsed_time {sec} DIV 60 {sec/min} ;
    date_time.minute := elapsed_time {min} MOD 60 {min} ;

    elapsed_time {hr} := elapsed_time {min} DIV 60 {min/hr} ;
    date_time.hour := elapsed_time {hr} MOD 24 {hr} ;

    month := base_system_time.month;
    year := base_system_time.year;

    IF pmp$this_is_a_leap_year (year) THEN
      days_in_the_month_p := ^v$leap_year;
    ELSE
      days_in_the_month_p := ^v$non_leap_year;
    IFEND;

    WHILE day < 1 DO
      IF month = 1 THEN
        month := 13;
        IF (year - 1 {yr} ) < LOWERVALUE (year) THEN
          RETURN;
        IFEND;
        year := year - 1;
        IF pmp$this_is_a_leap_year (year) THEN
          days_in_the_month_p := ^v$leap_year;
        ELSE
          days_in_the_month_p := ^v$non_leap_year;
        IFEND;
      IFEND;
      month := month - 1;
      day := day + days_in_the_month_p^ [month];
    WHILEND;

    WHILE day > days_in_the_month_p^ [month] DO
      day := day - days_in_the_month_p^ [month];
      month := month + 1 {mo} ;

      IF month > 12 {mo} THEN
        IF (year + 1 {yr} ) > UPPERVALUE (year) THEN
          RETURN;
        IFEND;

        month := 1 {mo} ;
        year := year + 1 {yr} ;

        IF pmp$this_is_a_leap_year (year) THEN
          days_in_the_month_p := ^v$leap_year;
        ELSE
          days_in_the_month_p := ^v$non_leap_year;
        IFEND;
      IFEND;
    WHILEND;

    date_time.day := day;
    date_time.month := month;
    date_time.year := year - 1900;

  PROCEND mtp$get_date_time_at_timestamp;







MODEND mtm$system_control;
