?? RIGHT := 110 ??
MODULE dsm$terminate_nos_ve ALIAS 'DSMTNVE';

*copy pxiotyp
?? PUSH (LISTEXT := ON) ??
*copy pxziobs
*copy bizput
*copy bizclos
*copy bizweor
*copy bizweof
*copy bizopen
*copy lgzgetp
*copy fzmark
*copy fzwords
*copy zutpw2h
*copyc zutpifl
?? POP ??
*copy dsc$constant_definitions
*copyc dst$deadstart_condition
*copyc dst$dft_request_codes
*copyc dst$dual_state_control_block_cc
*copy dsp$dst_global_variables
*copy dsc$job_control_registers
*copyc ost$hardware_subranges
*copy dsd$os_global_variables
*copyc dsp$callsda
*copyc dsp$callver

{  Define variables global to this module.

  VAR

{  Real memory word address for DFT request relative to pva type.

    dft_request_rmwa: ost$real_memory_address,

{  Byte rma of DFT request relative to pva type.

    dft_request_byte_rma: ost$real_memory_address,
    dft_request_r_pointer: dst$r_pointer,
    dft_request_pva_type: starting_pva;  { PVA type of area for DFT request.

{  Define constants global to this module.

    CONST
      normal_dft_response = 1;

*copy dsi$display_dayfile_message
*copy dsi$virtual_memory_access
*copyc dsi$support_eicb_version_4
*copy dsi$define_hardware_config
*copy dsi$deadstart_utilities
*copy dsi$k_display_control
*copy dsi$deadstart_command_processor
*copyc dsi$c170_access_to_ssr
*copyc dsi$termination_edd_dump
?? TITLE := 'determine_memory_limits', EJECT ??

{ PURPOSE:
{   Determine how much memory is assigned to NOS/VE.

    PROCEDURE determine_memory_limits;

      ver_request.general_status := 0;
      ver_request.length := 2;
      ver_request.cm_block.words_div_1000 := 0;
      ver_request.cm_block.lwa_div_1000 := 0;
      callver (ver_request, stcm, TRUE);

      IF ver_request.general_status = 1 THEN
        load_offset_bytes := ver_request.cm_block.fwa_div_1000 *
             bytes_per_octal_1k_words;
        nve_memory := ver_request.cm_block.lwa_div_1000 *
             bytes_per_octal_1k_words - load_offset_bytes;
      ELSE
        nve_memory := 0;
        load_offset_bytes := 0;
      IFEND;

    PROCEND determine_memory_limits;
?? TITLE := 'idle_secondary_ious', EJECT ??

{ PURPOSE:
{   This procedure idles all PPs (including DFT) in all secondary IOUs and
{   all channels are reset to the deadstart state.

  PROCEDURE idle_secondary_ious;

{  Define type definition for the DFT request to idle all PPs in secondary
{  IOU and reset channels to deadstart state.  In the DFT world this is a
{  64 bit structure but it is defined in 60 bits in the 170 world, each 60
{  bit word is moved into a 64 bit word right justified.

    TYPE
      dft_idle_iou_request = PACKED RECORD

{  Word 0.

        dft_response: 0 .. 0f(16),
        dft_function: 0 .. 0ff(16),
        iou_number: 0 .. 0ff(16),
        sub_function: 0 .. 0ff(16),
        fill: 0 .. 0ffffffff(16),
      RECEND;

    VAR
      cri {configuration record index}: integer,
      idle_iou_request: dft_idle_iou_request;

    cri := 1;

{  Initialize SDA request block to write DFT request.

    pp_table.dft_request_length := 1;
    pp_table.fill2 := 0;
    pp_table.ve_dft_request_p.offset := dft_request_r_pointer.offset;
    pp_table.ve_dft_request_p.rupper := dft_request_r_pointer.rupper;
    pp_table.ve_dft_request_p.rlower := dft_request_r_pointer.rlower;
    pp_table.fill4 := 0;
    pp_table.os_170_dft_request_p := ^idle_iou_request;

{  Initialize DFT request for idling secondary IOUs.

    idle_iou_request.dft_response := 0;
    idle_iou_request.dft_function := dsc$dft_idle_all_pps_and_chs;
    idle_iou_request.iou_number := 0;
    idle_iou_request.fill := 0;

    WHILE configuration_record [cri].iou.size > 0 DO

      CASE configuration_record [cri].iou.id OF

      = dsc$id_iou_info =
        IF idle_iou_request.iou_number > 0 THEN

{  Issue function to idle all PPs except DFT and set all channels in the
{  deadstart state.

          dyfstrnum ('IDLE IOU', idle_iou_request.iou_number, user_dayf);
          idle_iou_request.sub_function := 0;
          callsda (write_dft_request_block, pp_table);
          IF idle_iou_request.dft_response <> normal_dft_response THEN
            dyfstring (' Abnormal response idling PPs and CHS.', user_dayf);
          IFEND;

{  Issue DFT request to idle DFT in the secondary IOU.

          idle_iou_request.dft_response := 0;
          idle_iou_request.sub_function := 1;
          callsda (write_dft_request_block, pp_table);
          IF idle_iou_request.dft_response <> normal_dft_response THEN
            dyfstring (' Abnormal response idling DFT-S.', user_dayf);
          IFEND;
        IFEND;

        idle_iou_request.iou_number := idle_iou_request.iou_number + 1;
      ELSE

      CASEND;

      cri := cri + 1;
    WHILEND;

  PROCEND idle_secondary_ious;
?? TITLE := 'return_pps_channels', EJECT ??

{ PURPOSE:
{   Return peripheral processors and channels to c170 environment.

  PROCEDURE return_pps_channels;

    VAR
      d8st_word: dst$dscb_cc_d8st_word;

?? SKIP := 3 ??

    PROCEDURE idle_ve_pp
      (    pp: integer);

      ver_request.return_all := FALSE;
      ver_request.general_status := 0;
      ver_request.length := 1;
      ver_request.pp.primary := pp;
      ver_request.pp.kind := non_driver_pp;
      IF d7ty.eicb_version < dsc$eicb_version_4 THEN
         { use pre 2.5.1 code on ver request
         callver (ver_request, stpp, TRUE);
      ELSE
         { try new ( ie. NOS 2.5.1 ) request code
         callver (ver_request, stpt, TRUE);
      IFEND;
      IF ver_request.general_status <= 1 THEN
        pp_table.pp_number := pp;
        callsda (idle_pp, pp_table);
      IFEND;

    PROCEND idle_ve_pp;
?? SKIP := 3 ??

{  Check for and idle SCI and SCD prior to returning them via VER.
{  SCI is not returned if it is running as SCD or MDD for the host operating
{  system.

    get_dscb (dscb_d8st, ^d8st_word, 1);

    IF d8st_word.nosve_owns_sci_pp THEN

 {  SCI is not running as SCD or MDD for host operating system, return it.

      idle_ve_pp (d8st_word.sci_pp_number);
      d8st_word.sci_pp_number := 0;
      d8st_word.scd_port := 0;
      d8st_word.nosve_owns_sci_pp := FALSE;
    IFEND;

    idle_ve_pp (d8st_word.scd_pp_number);
    d8st_word.scd_pp_number := 0;
    d8st_word.operator_action := FALSE;

{  Return all channels to os.

    IF d7ty.eicb_version < dsc$eicb_version_4 THEN
    { use pre 2.5.1 code on ver request
       return_all (rtch);
    ELSE
    { use  2.5.1 code on ver request
       return_all (rnct);
    IFEND;

{  Return recovered pp to os.

    IF d7ty.eicb_version < dsc$eicb_version_4 THEN
    { use pre 2.5.1 code on ver request
       return_all (rtpp);
    ELSE
    { use  2.5.1 code on ver request
       return_all (rnpt);
    IFEND;

{  Update dual state control block.

    put_dscb (dscb_d8st, ^d8st_word, 1);

  PROCEND return_pps_channels;
?? TITLE := 'return_all', EJECT ??

  PROCEDURE return_all
    (    fcall: ver_functions);

    ver_request.return_all := TRUE;
    ver_request.general_status := 0;
    ver_request.length := 0;
    callver (ver_request, fcall, TRUE);
    IF ver_request.general_status > 1 THEN
      dyfstring ('Unable to return nve resources.', system_dayf);
    IFEND;

  PROCEND return_all;
?? TITLE := 'return_central_memory', EJECT ??

{ PURPOSE:
{   Return all central memory to the c170 operating system.

  PROCEDURE return_central_memory;

    VAR
      ssrptr: integer;

    jcr.global_r1 := $INTEGER (dsc$deadstart_condition_empty); { Inhibit recycle.
    ssrptr := 0;
    set_ei_pva (start_of_ve, 0);
    put_dscb (dscb_ssrptr, ^ssrptr, 1);
    set_ei_pva (start_of_ssr, 0);
    return_all (rtcm);
    dyfstring ('VE memory returned.', user_dayf);

  PROCEND return_central_memory;
?? TITLE := 'notify_operator', EJECT ??

  PROCEDURE [XDCL] notify_operator ALIAS 'DSPNO'
    (    msg: string ( * ));

    VAR
      i: integer;

    VAR
      s: string (40);

    s (1) := '$';
    s (2, 39) := msg;
    dyfstring (msg, system_dayf);
    dyfstring (s, bdisplay_line2);
    i := 0;
    REPEAT
      i := i + 1;
      wakeup;
    UNTIL i = 500;

  PROCEND notify_operator;
?? TITLE := 'terminate_cpu', EJECT ??

{ PURPOSE:
{   This procedure waits for the 170 trap handler to be running in EI, dumps NOS/VE
{   resources if specified and returns PPs, channels and equipment assigned
{   to NOS/VE.

  PROCEDURE terminate_cpu;

    VAR
      entry: integer,
      index: integer,
      left: integer,
      right: integer,
      save_global_r1: 0 .. 0777777(8);

    dyfstring ('cpu state switch', debug_log);

{  Wait for the 170 trap handler to be running in EI and not NOS/VE monitor.

    REPEAT
      deadstart_cpu (term_dual_state);
    UNTIL exitcd = 0;

{  Return all channels and equipment to host OS.  Need a tape channel and drive for dump.

    dyfstring ('returning channels and equipment.', debug_log);

    IF d7ty.eicb_version < dsc$eicb_version_4 THEN
    { use pre 2.5.1 code on ver request
       return_all (rtch);
    ELSE
    { use  2.5.1 code on ver request
       return_all (rnct);
    IFEND;

    return_all (rteq);

    IF take_dump THEN

{  Set global R1 to indicate dumping NOS/VE.  If abort while dumping because of tape
{  error will try again.  Restore to incoming value if dump completes successfully.

      save_global_r1 := jcr.global_r1;
      jcr.global_r1 := $INTEGER (error_dumping_nos_ve);
      jcrset;
      nosve_edd_dump;
      jcr.global_r1 := save_global_r1;
    IFEND;

    dyfstring ('returning pp-s.', debug_log);
    return_pps_channels;

  PROCEND terminate_cpu;
?? TITLE := 'dsptnve', EJECT ??

{ PURPOSE:
{   Begin NOS/VE termination, initial entry point for DSMTRM.

  PROGRAM [XDCL] dsptnve;

    VAR
      byeve: integer,
      dscb_ssr: dst$r_pointer,
      entry: integer,
      error: boolean,
      i: integer,
      left: integer,
      logical_cm_gt_256_mb: boolean,
      nve_memory_gt_64_mb: boolean,
      permsg: ^ARRAY [0 .. 54] OF string (40),
      ppbf_entry_offset: integer,
      ppbf_length: integer,
      ppbf_offset: integer,
      right: integer;

    makscpb; { make me a busy system controlpoint }

{  Prepare to read the command file.

    convert_title ('  TERMINATE VE  ');

{  Set line position for scrolling part of the screen.  This is set to
{  last line + 1 of the termination K display.  The maximum number allowed
{  for this value is 'header_line + 19'.

    line_position := header_line + 19;
    beginning_line_position := line_position;

{  Initialize K display control variables for VE termination.

    clear_screen;
    show_message ('NOS/VE TERMINATION IN PROGRESS.', header_line + 1,
          no_scroll);

{  Define file names and open dump file.

    px#iobs := large_jobs; { for faster transfers }

    jcrget;
    r1g_error_flag := $INTEGER (ok);
    IF jcr.global_error_flag = 77(8) THEN
      r1g_error_flag := jcr.global_r1; {deadstart error}
    ELSEIF jcr.global_error_flag <> 0 THEN
      r1g_error_flag := $INTEGER (fatal_170_error);
    IFEND;
    set_configuration;

{  Save the EICB version number from the EICB.  The EICB version number is used to
{  select which VER functions to use.

    get_dscb(dscb_d7ty,^d7ty, 1);
    got_eicb_d7ty:= TRUE;

{  Check for an SSR.

    get_dscb (dscb_ssrptr, ^dscb_ssr, 1);
    ssr_address_words := dscb_ssr.offset + dscb_ssr.rlower * 100(8) + dscb_ssr.
          rupper * 1000000(8);
    get_ve_status (i);
    IF (exitcd = 0) OR (ssr_address_words = 0) THEN

{  Get SSR address from EI and verify that it is the same as SSR address
{  from the EICB.

      get_ei_pva (start_of_ssr, i);
      IF i = 0 THEN
        IF ssr_address_words <> 0 THEN
          set_ei_pva (start_of_ssr, ssr_address_words);
        ELSE
          error_processor (invalid_ssr, warning);
        IFEND;
      ELSEIF i DIV 8 <> ssr_address_words THEN

{  Use SSR address that EI knows about.

        error_processor (invalid_ssr, warning);
      IFEND;

      IF ssr_address_words <> 0 THEN

{  Check first entry in SSR for validity, EI has ssr address set.

        get_ssr_directory_entry (0, left, right);
        IF (left = 0) OR (left > 100) THEN
          ssr_address_words := 0;
          error_processor (invalid_ssr, warning);
        IFEND;
      IFEND;
    IFEND;

{  Start NOS/VE termination processing.

    byeve := 0;
    terminate_nve_job := FALSE;
    run_mode := (jcr.global_error_flag = 0);
    determine_memory_limits;
    nve_memory_gt_64_mb := (nve_memory DIV 100000(16) > 64);
    logical_cm_gt_256_mb := (((nve_memory + load_offset_bytes) DIV 100000(16)) > 256);
    get_dscb (dscb_cptptr, ^cptp_r_pointer, 1);

{  Set amount of memory to be dumped to the default.

    IF nve_memory_gt_64_mb AND (cptp_r_pointer.length <> 0) THEN
      memory_to_be_dumped := 'CRITICAL';
      critical_dump_allowed := TRUE;
    ELSE
      memory_to_be_dumped := 'ALL';
      critical_dump_allowed := FALSE;
    IFEND;

    IF jcr.global_error_flag <> 0 THEN

{  Check for orderly termination.

      IF ssr_address_words <> 0 THEN
        find_ssr_entry (dsc$ssr_termination_status, entry);
        get_ssr_directory_entry (entry, left, byeve);
        dyfstrnum ('byve=', byeve, user_dayf);
      IFEND;
      IF (byeve <> 0) AND (r1g_error_flag = $INTEGER (nosve_down)) THEN
        run_mode := TRUE;
        jcr.global_error_flag := 0;
      ELSE
        permsg := #LOC (ds_error_messages);
        show_message (permsg^ [r1g_error_flag], header_line, no_scroll);
      IFEND;

      IF logical_cm_gt_256_mb THEN
        show_message ('   IT IS NOT POSSIBLE TO HAVE THE NVE SUBSYSTEM TAKE',
              header_line + 1, no_scroll);
        show_message ('   A DUMP WHEN THE LOGICAL SIZE OF MEMORY EXCEEDS',
              header_line + 2, no_scroll);
        show_message ('   256 MB.  IF A DUMP IS DESIRED, TAKE AN',
              header_line + 3, no_scroll);
        show_message ('   EXPRESS DEADSTART DUMP (EDD) NOW.',
              header_line + 4, no_scroll);
        show_message ('   (USE OF EDD WILL INTERRUPT SYSTEM OPERATION.)',
              header_line + 5, no_scroll);
        show_message (' ', header_line + 6, no_scroll);

        show_message ('   PERFORM THESE STEPS TO CONTINUE PROCESSING.',
              header_line + 7, no_scroll);
        show_message (' ', header_line + 8, no_scroll);
        show_message ('   1. SELECT A VALUE FOR THE FOLLOWING OPTION:',
              header_line + 9, no_scroll);
        show_message ('       (*TNVEJOB=FALSE. IS THE DEFAULT.)',
              header_line + 10, no_scroll);
        show_message ('       *TNVEJOB=TRUE.  TERMINATE THE NVE JOB.',
              header_line + 11, no_scroll);
        show_message ('       *TNVEJOB=FALSE. DO NOT TERMINATE THE NVE JOB.',
              header_line + 12, no_scroll);
        show_message (' ', header_line + 13, no_scroll);
        show_message (' ', header_line + 14, no_scroll);
      ELSE
        show_message ('   PERFORM THESE STEPS TO CONTINUE PROCESSING.',
              header_line + 1, no_scroll);
        show_message (' ', header_line + 2, no_scroll);
        show_message ('   1. SELECT VALUES FOR THE FOLLOWING OPTIONS:',
              header_line + 3, no_scroll);
        IF nve_memory_gt_64_mb AND (cptp_r_pointer.length <> 0) THEN
          show_message ('       (*DUMP=CRITICAL. *VSN=DMP00A. *DENSITY=GE. AND',
                header_line + 4, no_scroll);
        ELSE
          show_message ('       (*DUMP=ALL. *VSN=DMP00A. *DENSITY=GE. AND',
                header_line + 4, no_scroll);
        IFEND;

        show_message ('       *TNVEJOB=FALSE. ARE THE DEFAULTS.)',
              header_line + 5, no_scroll);
        show_message (' ', header_line + 6, no_scroll);
        show_message ('       *DUMP=ALL/TRUE. DUMP ALL CENTRAL MEMORY.',
              header_line + 7, no_scroll);

        IF nve_memory_gt_64_mb AND (cptp_r_pointer.length <> 0) THEN
          show_message ('       *DUMP=CRITICAL. DUMP CRITICAL MEMORY.',
                header_line + 8, no_scroll);
        ELSE
          show_message (' ', header_line + 8, no_scroll);
        IFEND;

        show_message ('       *DUMP=NONE.     DO NOT DUMP CENTRAL MEMORY.',
              header_line + 9, no_scroll);
        show_message ('       *DUMP=FALSE.    DO NOT GENERATE DUMP FILE.',
              header_line + 10, no_scroll);
        show_message ('       *DENSITY=PE/GE. DUMP TAPE DENSITY, PE OR GE.',
              header_line + 11, no_scroll);
        show_message ('       *VSN=XXXXXX.    DUMP TAPE VSN.',
              header_line + 12, no_scroll);
        show_message ('       *TNVEJOB=TRUE.  TERMINATE THE NVE JOB.',
              header_line + 13, no_scroll);
        show_message ('       *TNVEJOB=FALSE. DO NOT TERMINATE THE NVE JOB.',
              header_line + 14, no_scroll);

      IFEND;
      show_message (' ', header_line + 15, no_scroll);
      show_message ('   2.ENTER', header_line + 16, no_scroll);
      show_message ('       *RUN.', header_line + 17, no_scroll);

{  Last line, this line number plus 1 should be set as line position earlier
{  in this procedure.

      show_message (' ', header_line + 18, no_scroll);
    IFEND;

    IF NOT logical_cm_gt_256_mb THEN
      take_dump := (NOT run_mode);
    ELSE
      take_dump := FALSE
    IFEND;

    IF (sw2 IN jcr.sense_switches) OR (NOT run_mode) THEN
      display; {request k display in idle}
      wait_for_operator_action (logical_cm_gt_256_mb, error);
    IFEND;

    clear_screen;
    show_message ('TERMINATING NOS/VE.', header_line + 1, no_scroll);


{  Ensure that NOS/VE has terminated.

    jcr.r3 := jcr.global_r1;

{  Set flag that determines if memory returned and whether the NVE 170 job
{  should continue.  Initialize to return memory and terminate NVE 170 job.

    jcr.global_r1 := $INTEGER (dsc$deadstart_condition_empty);

    IF ssr_address_words <> 0 THEN

{  Set up r pointer for the DFT requests that are passed through SDA.  SDA
{  will move the DFT requests here and the area after the DFT request can
{  be used for information that DFT will return for the request.  Use PPBF
{  in the SSR for DFT requests and the returned information.

      find_ssr_entry (dsc$ssr_pp_controlware_buf, ppbf_entry_offset);
      get_ssr_directory_entry (ppbf_entry_offset, ppbf_length, ppbf_offset);
      dft_request_rmwa := ssr_address_words + ppbf_offset;
      dft_request_pva_type := start_of_ssr;
      dft_request_byte_rma := (ppbf_offset * 8);

      dft_request_r_pointer.offset := dft_request_rmwa MOD 100(8);
      dft_request_r_pointer.rlower := (dft_request_rmwa DIV 100(8))
            MOD 10000(8);
      dft_request_r_pointer.rupper := dft_request_rmwa DIV 1000000(8);
      dft_request_r_pointer.length := ppbf_length;

      terminate_cpu;

{  Return appropriate resources and determine what kind if any deadstart
{  to do next.

      find_ssr_entry (dsc$ssr_deadstart_type, entry);
      get_ssr_directory_entry (entry, left, right);
      dyfstrnum ('dst_type=', right, user_dayf);
      jcr.global_r1 := right;

      IF byeve = 0 THEN
        {This worries me - will we ALWAYS retain memory when required ???
        set_ssr_directory_entry (entry, left,
            $INTEGER (dsc$continuation_deadstart));
        dyfstring ('continuation forced due to nosve failure', user_dayf);
      IFEND;
    IFEND;

    return_pps_channels;
    return_all (rteq);
    idle_secondary_ious;

    IF jcr.global_r1 = $INTEGER (dsc$deadstart_condition_empty) THEN
      callsda (clear_nos_ve_dft_buffer_p, pp_table);
      return_central_memory;
      dyfstring ('nos/ve aborted.', user_dayf);
    ELSEIF terminate_nve_job THEN
      dyfstring ('NVE JOB TERMINATED BY COMMAND.', user_dayf);
      dyfstring ('  NOS/VE MEMORY NOT RETURNED.', user_dayf);

{  Set flag to terminate the NVE 170 job.

      jcr.global_r1 := $INTEGER (dsc$deadstart_condition_empty);
    ELSE
      dyfstring ('nve continuing.', user_dayf);
    IFEND;

{  Set global error flag to normal termination and write CCL registers
{  to communicate with CCL part of NVE job.

    jcr.global_error_flag := 0;
    jcrset;
    endprgr;             { nos/be scp status dropped

  PROCEND dsptnve;
MODEND dsm$terminate_nos_ve;
