?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Display Processor : System Console Monitor' ??
MODULE dpm$system_console_monitor;

{ PURPOSE:
{   This module contains the routines for driving the system console and processing system console monitor
{   requests.  This consists mainly of updating the screens and processing keyboard input.
{
{ NOTE:
{   Some of the routines in this module are called periodically from the monitor loop or to process an
{   external interrupt from the system console driver.  Because of this it is important that this code is as
{   efficient as possible.

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc dpc$console_row_size
*copyc dpc$top_line_message_size
*copyc dpe$error_codes
*copyc dpt$console_parameter_block
*copyc dpt$critical_messages
*copyc dpt$critical_window_date_time
*copyc dpt$lock_main_window
*copyc dpt$rb_display_request
*copyc dpt$scd_communications_block
*copyc dpt$secure_input_line
*copyc dpt$window
*copyc osc$processor_defined_registers
*copyc oss$mainframe_wired
*copyc oss$mainframe_wired_cb
*copyc syc$monitor_segment_numbers
?? POP ??
*copyc dpp$process_monitor_command
*copyc dsp$mtr_get_ssr_data_seq_ptr
*copyc i#mtr_disable_traps
*copyc i#mtr_restore_traps
*copyc i#test_set_bit
*copyc mtp$error_stop
*copyc mtp$get_date_time_at_timestamp
*copyc mtp$set_status_abnormal
*copyc tmp$check_ptl_lock
*copyc tmv$ptl_lock
*copyc tmp$set_system_flag
?? EJECT ??
*copyc dpv$scd_block_p
*copyc dpv$scd_time
*copyc mtv$idle_message_line
*copyc mtv$nst_p
*copyc mtv$scb
*copyc osv$external_interrupt_selector
*copyc tmv$system_job_monitor_gtid
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??

  TYPE
    t$console_row_map = RECORD
      window_id: dpt$window_id,
      row_p: ^dpt$console_line,
    RECEND,

    t$console_row_mapping = ARRAY [1 .. dpc$number_of_console_rows] OF t$console_row_map,

    t$echo_input_line = RECORD
      row_number: ALIGNED [0 MOD 8] dpt$number_of_console_rows,
      column_number: 0 .. 132,
      text_size: dpt$console_row_size,
      unused: dpt$console_row_size,
      next_line_rma: ost$real_memory_address,
      text: string (dpc$console_row_size),
    RECEND;
?? EJECT ??
  VAR
    dpv$180_operator_action: [XDCL, #GATE] boolean := FALSE,
    dpv$critical_display_id: [XDCL, #GATE] dpt$window_id := 0,
    dpv$critical_messages: [XDCL, STATIC, #GATE, oss$mainframe_wired] dpt$critical_messages :=
           [REP 15 OF [0, '']],
    dpv$critical_msgs_need_logging: [XDCL, STATIC, #GATE, oss$mainframe_wired] boolean := FALSE,
    dpv$enable_console_bell: [XDCL, #GATE] boolean := TRUE,
    dpv$enable_stop_key: [XDCL, #GATE] boolean := FALSE,
    dpv$lock: [XDCL] integer := 0,
    dpv$lock_main_window: [XDCL, #GATE] dpt$lock_main_window := [FALSE, 0],
    dpv$secure_input_line: [XDCL, #GATE] dpt$secure_input_line := [FALSE, 0],
    dpv$top_window_p: [XDCL, #GATE] ^dpt$window := NIL,

    syv$db_page_wait_lines_instance: [XDCL, #GATE] integer := 0,
    syv$debugger_display_id: [XDCL, #GATE] dpt$window_id := 0,
    syv$terminate_sysdebug_output: [XDCL, #GATE] boolean := FALSE,

    v$console_row_mapping: t$console_row_mapping := [REP dpc$number_of_console_rows OF [0, NIL]],
    v$deleted_windows_p: ^dpt$window := NIL,
    v$echo_input_request: dpt$ve_data_for_scd := [dpc$scd_echo_input, FALSE, 0, 0, 0],
    v$echo_input_line: [STATIC, oss$mainframe_wired_cb] t$echo_input_line := [30, 1, 0, 0, 0, ' '],
    v$end_of_queue_p: ^dpt$console_line := NIL,
    v$end_pause: integer := 0,
    v$expanded_window_p: ^dpt$window := NIL,
    v$expansions: integer := 0,
    v$last_scd_rma: ost$real_memory_address := 0,
    v$next_window_ordinal: dpt$window_id := 1,
    v$previous_scd_data_id: 0 .. 255 := 0,
    v$re_map_windows: boolean := FALSE,
    v$scd_queue_p: ^dpt$console_line := NIL,
    v$screen_ready: boolean := FALSE,
    v$start_of_queue_p: ^dpt$console_line := NIL;

?? OLDTITLE ??
?? NEWTITLE := 'clean_up_display_queue', EJECT ??

{ PURPOSE:
{   This procedure scans the SCD display queue removing all lines from the display queue that SCD has
{   processed.

  PROCEDURE [INLINE] clean_up_display_queue;

    VAR
      rma: ost$real_memory_address;

    { Retrieve the real memory word address of the last line SCD processed.

    rma := dpv$scd_block_p^.scd.current_data_rma MOD 80000000(16);

    IF v$last_scd_rma = rma THEN
      RETURN;
    IFEND;

    { Remove display lines from the queue that SCD has processed.

    WHILE v$scd_queue_p^.next_line_rma <> rma DO
      v$scd_queue_p^.next_line_rma := dpc$rma_scd_finished;
      v$scd_queue_p := v$scd_queue_p^.next_line_p;
    WHILEND;

    { Remove the last line that SCD processed.

    v$scd_queue_p^.next_line_rma := dpc$rma_scd_finished;
    v$scd_queue_p := v$scd_queue_p^.next_line_p;

    { Save the last real memory word address if the queue was not empty.

    IF v$scd_queue_p <> NIL THEN
      v$last_scd_rma := rma;
    ELSE
      v$last_scd_rma := 0;
    IFEND;

  PROCEND clean_up_display_queue;
?? OLDTITLE ??
?? NEWTITLE := 'configure_console', EJECT ??

{  PURPOSE:
{    This procedure initializes the SCI parameter table so that NOS/VE and SCI can communicate.

  PROCEDURE configure_console;

    VAR
      console_block_p: ^dpt$console_parameter_block,
      rma: integer,
      scipt_seq_p: ^SEQ ( * );

    { The SCI parameter table is pointed to by word D7RS+2 in the EICB.

    { Set the pointer to the SCI parameter table which is pointed to by word D7RS+2 in the EICB or it is the
    { SSR.  A bit in D7RS tells NOS/VE where the parameter table is located.

    IF mtv$nst_p^.d7rs2.scipt_in_the_ssr THEN
      dsp$mtr_get_ssr_data_seq_ptr (dsc$ssr_sci_parameter_table, scipt_seq_p);
      console_block_p := #ADDRESS (1, #SEGMENT (scipt_seq_p), #OFFSET (scipt_seq_p));
    ELSE
      console_block_p := #ADDRESS (1, syc$msn_cyber_170_cache_bypass, mtv$nst_p^.d7rs2.sci_table * 8);
    IFEND;

    #real_memory_address (^mtv$idle_message_line, rma);
    dpv$scd_block_p^.top_line_message_rma := rma DIV 8;

    #real_memory_address (dpv$scd_block_p, rma);

    { Interlock the SCI parameter table.

    set_sci_interlock (console_block_p);
    console_block_p^.primary_ve_interface.ve_parameter_block := rma DIV 8;
    console_block_p^.primary_ve_interface.interrupt_selector := osv$external_interrupt_selector;
    console_block_p^.scd_console.nos_ve_active := TRUE;
    console_block_p^.scd_console.console_active := TRUE;
    console_block_p^.scd_definition_changed := TRUE;
    console_block_p^.interlocked := FALSE;

  PROCEND configure_console;
?? OLDTITLE ??
?? NEWTITLE := 'map_windows_onto_console', EJECT ??

{ PURPOSE:
{   This procedure maps the current open windows onto the system console screen.
{
{ DESIGN:
{   There are more lines in all of the windows than will probably fit on the screen.  The screen is painted
{   from the top window to the last window putting the defined number of lines in each window.  Pre-emptive
{   windows can reduce the number of lines in other display windows because as the name suggests they pre-empt
{   other windows.  Only one pre-emptive window is displayed.  The title line, from the other pre-emptive
{   windows, is the only line displayed from those windows.

  PROCEDURE map_windows_onto_console;

    VAR
      display_interactive_line: boolean,
      ending_console_row_number: dpt$number_of_console_rows,
      ending_row: dpt$number_of_console_rows,
      interactive_window_count: dpt$window_id,
      line_index: dpt$number_of_window_lines,
      lines_not_displayed: dpt$number_of_console_rows,
      next_available_row: dpt$number_of_console_rows,
      number_of_extra_rows: dpt$number_of_console_rows,
      number_of_required_rows: integer,
      pre_emptive_window_exists: boolean,
      row_index: dpt$number_of_console_rows,
      rows_available: boolean,
      rows_for_pre_empt: dpt$number_of_console_rows,
      rows_for_sharing_window: dpt$number_of_console_rows,
      rows_needed_for_expand: dpt$number_of_console_rows,
      sharing_window_count: dpt$window_id,
      starting_row: dpt$number_of_console_rows,
      temp_ending_row: integer,
      window_line_number: dpt$number_of_window_lines,
      window_p: ^dpt$window;

    { Abort any output that SCD is in the process of printing on the console.  Since the screen is about to
    { be repainted and the information is saved in the window descriptors, this output can be aborted.

    clean_up_display_queue;
    IF v$scd_queue_p <> NIL THEN
      dpv$scd_block_p^.ve.command := dpc$scd_abort_output;
    IFEND;
    v$last_scd_rma := 0;
    v$start_of_queue_p := NIL;
    v$end_of_queue_p := NIL;
    v$scd_queue_p := NIL;

    { Determine how many shared and interactive windows exist and determine if a pre-emptive window exists.

    pre_emptive_window_exists := FALSE;
    interactive_window_count := 0;
    sharing_window_count := 0;
    window_p := dpv$top_window_p;
    WHILE window_p <> NIL DO
      IF window_p^.class = dpc$wc_sharing THEN
        sharing_window_count := sharing_window_count + 1;
        IF window_p^.kind = dpc$wk_interactive THEN
          interactive_window_count := interactive_window_count + 1;
        IFEND;
      IFEND;
      pre_emptive_window_exists := pre_emptive_window_exists OR (window_p^.class = dpc$wc_pre_empt);
      window_p := window_p^.next_window_p;
    WHILEND;

    { Determine the number of extra rows available that will be spread among the 'shared' windows.

    number_of_required_rows := sharing_window_count + (interactive_window_count * 3) + 1;
    IF number_of_required_rows > dpc$number_of_console_rows THEN
      number_of_extra_rows := 0;
    ELSE
      number_of_extra_rows := dpc$number_of_console_rows - number_of_required_rows;
    IFEND;
    IF pre_emptive_window_exists THEN
      IF number_of_extra_rows = 0 THEN
        rows_for_pre_empt := 0;
      ELSE
        IF number_of_extra_rows < 10 THEN
          rows_for_pre_empt := number_of_extra_rows - 1;
          number_of_extra_rows := 0;
        ELSE
          rows_for_pre_empt := 9;
          number_of_extra_rows := number_of_extra_rows - 10;
        IFEND;
      IFEND;
    ELSE
      rows_for_pre_empt := 0;
    IFEND;

    { Determine how many rows are needed for the expanded window, if one exists.

    IF (v$expanded_window_p <> NIL) AND (v$expanded_window_p^.class = dpc$wc_sharing) THEN
      IF (v$expansions * 5) > number_of_extra_rows THEN
        rows_needed_for_expand := number_of_extra_rows;
      ELSE
        rows_needed_for_expand := v$expansions * 5;
      IFEND;
      number_of_extra_rows := number_of_extra_rows - rows_needed_for_expand;
    ELSE
      rows_needed_for_expand := 0;
      v$expansions := 0;
      v$expanded_window_p := NIL;
    IFEND;

    { Determine the number of rows each sharing window will receive.

    IF sharing_window_count > interactive_window_count THEN
      rows_for_sharing_window := number_of_extra_rows DIV (sharing_window_count - interactive_window_count);
    ELSE
      rows_for_sharing_window := 0;
    IFEND;

    { Allocate the console rows for each window and set up an array with the window identifier and row
    { pointer for each line.  As each line is added to the screen it is added to the queue that SCD will
    { display.

    window_p := dpv$top_window_p;
    v$console_row_mapping [1].window_id := window_p^.window_id;
    v$console_row_mapping [1].row_p := ^window_p^.title;
    next_available_row := 2;

  /main_loop/
    WHILE window_p <> NIL DO
      IF window_p^.class = dpc$wc_invisible THEN
        window_p^.starting_console_row_number := 0;
        window_p^.ending_console_row_number := 0;
        FOR line_index := 1 TO dpc$number_of_window_lines DO
          window_p^.lines [line_index].starting_console_row_number := 0;
          window_p^.lines [line_index].ending_console_row_number := 0;
          window_p^.lines [line_index].next_line_rma := dpc$rma_scd_finished;
        FOREND;
        window_p := window_p^.next_window_p;
        CYCLE /main_loop/;
      IFEND;

      rows_available := (next_available_row < dpc$number_of_console_rows);

      IF rows_available THEN

        { Determine the starting row and the ending row for the window.

        starting_row := next_available_row;
        display_interactive_line := TRUE;

        IF window_p^.class = dpc$wc_sharing THEN
          window_p^.title.text_kind := dpc$tk_title;
          IF window_p^.kind = dpc$wk_interactive THEN
            temp_ending_row := starting_row + 3;
          ELSE
            temp_ending_row := starting_row + rows_for_sharing_window;
          IFEND;
        ELSE { window_p^.class = dpc$wc_pre_empt }
          IF pre_emptive_window_exists THEN
            window_p^.title.text_kind := dpc$tk_flashing_title;
            temp_ending_row := starting_row + rows_for_pre_empt;
            pre_emptive_window_exists := FALSE;
          ELSE { Display only title line on subsequent preemptive windows. }
            window_p^.title.text_kind := dpc$tk_flashing_title;
            temp_ending_row := starting_row;
            display_interactive_line := FALSE;
          IFEND;
        IFEND;

        IF v$expanded_window_p = window_p THEN
          temp_ending_row := temp_ending_row + rows_needed_for_expand;
        IFEND;

        IF (window_p^.next_window_p = NIL) OR (temp_ending_row >= dpc$number_of_console_rows) THEN
          ending_row := dpc$number_of_console_rows;
          next_available_row := ending_row;
        ELSE
          ending_row := temp_ending_row;
          next_available_row := ending_row + 1;
        IFEND;

        { Associate the window id with each row for the window.

        FOR row_index := starting_row TO ending_row DO
          v$console_row_mapping [row_index].window_id := window_p^.window_id;
        FOREND;

        { Setup the title for the window.

        window_p^.title.starting_console_row_number := 0;
        window_p^.title.ending_console_row_number := starting_row;
        v$console_row_mapping [starting_row].row_p := ^window_p^.title;
        queue_line (window_p^.title);
        starting_row := starting_row + 1;

        { Set the input line as the last line in the interactive window.

        IF (window_p^.kind = dpc$wk_interactive) AND display_interactive_line THEN
          window_p^.input_line.starting_console_row_number := 0;
          window_p^.input_line.ending_console_row_number := ending_row;
          v$console_row_mapping [ending_row].row_p := ^window_p^.input_line;
          queue_line (window_p^.input_line);
          ending_row := ending_row - 1;
        IFEND;

        window_p^.starting_console_row_number := starting_row;
        window_p^.ending_console_row_number := ending_row;

        { If necessary, readjust the number of lines in a displayed page for the System Core Debugger.

        IF window_p^.window_id = syv$debugger_display_id THEN
          syv$db_page_wait_lines_instance := ending_row - starting_row;
        IFEND;

        { Set the number of lines in the window that are not being displayed.

        lines_not_displayed := starting_row - ending_row + dpc$number_of_console_rows - 1;

        IF (window_p^.kind = dpc$wk_table) OR (starting_row > ending_row) THEN
          ending_console_row_number := 0;
          window_line_number := 1;
        ELSE { Set the present window line to allow for scrolling. }
          ending_console_row_number := ending_row;
          window_line_number := (window_p^.present_window_line_number + lines_not_displayed) MOD
                dpc$number_of_window_lines + 1;
        IFEND;

        { Add lines to the current queue of lines being displayed.

        FOR row_index := starting_row TO ending_row DO
          window_p^.lines [window_line_number].starting_console_row_number := 0;
          window_p^.lines [window_line_number].ending_console_row_number := row_index;
          v$console_row_mapping [row_index].row_p := ^window_p^.lines [window_line_number];
          queue_line (window_p^.lines [window_line_number]);
          window_line_number := window_line_number MOD dpc$number_of_window_lines + 1;
        FOREND;

      ELSE
        lines_not_displayed := dpc$number_of_console_rows;
        ending_console_row_number := 0;
        window_line_number := 1;
      IFEND;

      { Reset lines in window that are not being displayed.

      FOR row_index := 1 TO lines_not_displayed DO
        window_p^.lines [window_line_number].starting_console_row_number := 0;
        window_p^.lines [window_line_number].ending_console_row_number := ending_console_row_number;
        window_p^.lines [window_line_number].next_line_rma := dpc$rma_scd_finished;
        window_line_number := window_line_number MOD dpc$number_of_window_lines + 1;
      FOREND;

      window_p := window_p^.next_window_p;
    WHILEND /main_loop/;

    { Remove the deleted windows from the active window queue.

    WHILE v$deleted_windows_p <> NIL DO
      window_p := v$deleted_windows_p;
      v$deleted_windows_p := window_p^.next_window_p;
      window_p^.window_id := 0;
    WHILEND;

    v$screen_ready := TRUE;

  PROCEND map_windows_onto_console;
?? OLDTITLE ??
?? NEWTITLE := 'move_window', EJECT ??

{ PURPOSE:
{   Move the specified window to the window following the top window.

  PROCEDURE move_window
    (VAR window_to_move_p: ^dpt$window);

    VAR
      search_window_p: ^dpt$window;

    IF dpv$top_window_p = window_to_move_p THEN
      RETURN;
    IFEND;

    search_window_p := dpv$top_window_p;
    WHILE search_window_p^.next_window_p <> window_to_move_p DO
      search_window_p := search_window_p^.next_window_p;
    WHILEND;

    search_window_p^.next_window_p := window_to_move_p^.next_window_p;
    window_to_move_p^.next_window_p := dpv$top_window_p^.next_window_p;
    dpv$top_window_p^.next_window_p := window_to_move_p;

  PROCEND move_window;
?? OLDTITLE ??
?? NEWTITLE := 'process_keyboard_input', EJECT ??

{ PURPOSE:
{   This procedure processes the keyboard input from SCD.

  PROCEDURE process_keyboard_input
    (    character: 0 .. 255;
     VAR character_processed: boolean);

    CONST

      { Codes:  00(16) - 1F(16) are ASCII control characters.

      c$acc_bell = 07(16),
      c$acc_cursor_left = 08(16),
      c$acc_tab = 09(16),
      c$acc_line_feed = 0A(16),
      c$acc_clear_eol = 0B(16),
      c$acc_clear_page = 0C(16),
      c$acc_carriage_return = 0D(16),
      c$acc_cursor_up = 17(16),
      c$acc_cursor_right = 18(16),
      c$acc_home = 19(16),
      c$acc_cursor_down = 1A(16),
      c$acc_erase = 1F(16),

      { Codes:  20(16) - 7f(16) are ASCII characters.

      { Codes:  80(16) - 0FF(16) are special Function keys.  All of the special functions key codes actually
      { contain several HEX codes.  The first code is always 1E(16).  Instead of having SCD send all of the
      { codes it sends only the last code with the leftmost bit set indicating that it is a multi code
      { function key.  Example:  Back Key = 1E(16) & 5F(16).  SCD sends 0DF(16).

      c$key_print = 082(16),
      c$key_down = 0A0(16),
      c$key_up = 0A4(16),
      c$key_fwd = 0A8(16),
      c$key_bkw = 0AC(16),
      c$key_stop = 0C9(16),
      c$key_back = 0DF(16),
      c$key_shifted_super = 0E9(16),
      c$key_f4 = 0F4(16),
      c$key_super = 0F9(16),
      c$key_sub = 0FA(16),

      c$cursor_bias = 31,
      c$scd_position_cursor = 2;

    VAR
      ignore_status: syt$monitor_status,
      line_p: ^dpt$console_line,
      lines_in_window: dpt$number_of_window_lines,
      next_console_row: dpt$number_of_console_rows,
      ptl_interlocked: boolean,
      save_next_console_row: dpt$number_of_console_rows,
      table_starting_line: integer,
      window_id: dpt$window_id,
      window_p: ^dpt$window;

    character_processed := TRUE;

    { Determine if the screen is ready for input, if not sound the bell.

    IF NOT v$screen_ready THEN
      v$echo_input_line.text (v$echo_input_line.text_size + 1) := $CHAR (c$acc_bell);
      v$echo_input_line.text_size := v$echo_input_line.text_size + 1;
      RETURN;
    IFEND;

    { Retrieve the window id of the input line and retrieve the input line itself.

    window_id := v$console_row_mapping [v$echo_input_line.row_number].window_id;
    line_p := v$console_row_mapping [v$echo_input_line.row_number].row_p;

    { Inform SCD to hold the display until all input is received.

    IF NOT v$echo_input_request.hold_display_for_input THEN
      v$echo_input_line.text (v$echo_input_line.text_size + 1) := $CHAR (c$scd_position_cursor);
      v$echo_input_line.text (v$echo_input_line.text_size + 2) :=
            $CHAR (v$echo_input_line.column_number + c$cursor_bias);
      v$echo_input_line.text (v$echo_input_line.text_size + 3) :=
            $CHAR (v$echo_input_line.row_number + c$cursor_bias);
      v$echo_input_line.text_size := v$echo_input_line.text_size + 3;
      v$echo_input_request.hold_display_for_input := TRUE;
    IFEND;

    { Process any ASCII keyboard input (ASCII characters) and echo back the character to the console.

    IF (character >= 20(16)) AND (character <= 7F(16)) THEN
      IF dpv$lock_main_window.lock AND (dpv$lock_main_window.window_id = window_id) THEN
        v$echo_input_request.hold_display_for_input := FALSE;
        RETURN;
      IFEND;
      IF (line_p^.text_kind <> dpc$tk_input) OR
            (v$echo_input_line.column_number >= (dpc$console_row_size - 1)) THEN
        v$echo_input_line.text (v$echo_input_line.text_size + 1) := $CHAR (c$acc_bell);
      ELSE
        line_p^.text (v$echo_input_line.column_number) := $CHAR (character);
        IF line_p^.text_size < v$echo_input_line.column_number THEN
          line_p^.text_size := v$echo_input_line.column_number;
        IFEND;
        v$echo_input_line.column_number := v$echo_input_line.column_number + 1;
        IF dpv$secure_input_line.secure AND (dpv$secure_input_line.window_id = window_id) THEN
          v$echo_input_line.text (v$echo_input_line.text_size + 1) := ' ';
        ELSE
          v$echo_input_line.text (v$echo_input_line.text_size + 1) := $CHAR (character);
        IFEND;
      IFEND;
      v$echo_input_line.text_size := v$echo_input_line.text_size + 1;
      RETURN;
    IFEND;

    { Find the window needed for the special function key.

    IF character > 80(16) THEN
      window_p := dpv$top_window_p;
      WHILE (window_p <> NIL) AND (window_p^.window_id <> window_id) DO
        window_p := window_p^.next_window_p;
      WHILEND;
      IF window_p = NIL THEN
        character_processed := FALSE;
        v$echo_input_request.hold_display_for_input := FALSE;
        RETURN;
      IFEND;
    IFEND;

    CASE character OF
    = c$acc_cursor_left =
      IF v$echo_input_line.column_number = 1 THEN
        v$echo_input_line.column_number := dpc$console_row_size;
        IF v$echo_input_line.row_number = 1 THEN
          v$echo_input_line.row_number := dpc$number_of_console_rows;
        ELSE
          v$echo_input_line.row_number := v$echo_input_line.row_number - 1;
        IFEND;
      ELSE
        v$echo_input_line.column_number := v$echo_input_line.column_number - 1;
      IFEND;
      v$echo_input_line.text (v$echo_input_line.text_size + 1) := $CHAR (c$acc_cursor_left);
      v$echo_input_line.text_size := v$echo_input_line.text_size + 1;
      RETURN;

    = c$acc_tab =
      v$echo_input_line.column_number := 1;
      next_console_row := v$echo_input_line.row_number MOD dpc$number_of_console_rows + 1;
      save_next_console_row := next_console_row;
      window_id := v$console_row_mapping [next_console_row].window_id;
      REPEAT
        v$echo_input_line.row_number := next_console_row;
        next_console_row := next_console_row MOD dpc$number_of_console_rows + 1;
      UNTIL (v$console_row_mapping [next_console_row].window_id <> window_id) OR
            (next_console_row = save_next_console_row);
      v$echo_input_line.text (v$echo_input_line.text_size + 1) := $CHAR (c$scd_position_cursor);
      v$echo_input_line.text (v$echo_input_line.text_size + 2) :=
            $CHAR (v$echo_input_line.column_number + c$cursor_bias);
      v$echo_input_line.text (v$echo_input_line.text_size + 3) :=
            $CHAR (v$echo_input_line.row_number + c$cursor_bias);
      v$echo_input_line.text_size := v$echo_input_line.text_size + 3;
      RETURN;

    = c$acc_clear_eol =
      IF (line_p^.text_kind = dpc$tk_input) OR (line_p^.text_kind = dpc$tk_input_ready) THEN
        line_p^.text_size := v$echo_input_line.column_number - 1;
        line_p^.text (v$echo_input_line.column_number, * ) := ' ';
        line_p^.text_kind := dpc$tk_input;
        v$echo_input_line.text (v$echo_input_line.text_size + 1) := $CHAR (c$acc_clear_eol);
      ELSE
        v$echo_input_line.text (v$echo_input_line.text_size + 1) := $CHAR (c$acc_bell);
      IFEND;
      v$echo_input_line.text_size := v$echo_input_line.text_size + 1;
      RETURN;

    = c$acc_clear_page =
      v$echo_input_line.text (1) := $CHAR (c$acc_clear_page);
      v$echo_input_line.text_size := 1;
      v$echo_input_request.hold_display_for_input := FALSE;
      v$re_map_windows := TRUE;
      RETURN;

    = c$acc_carriage_return =
      IF dpv$lock_main_window.lock AND (dpv$lock_main_window.window_id = window_id) THEN
        v$echo_input_request.hold_display_for_input := FALSE;
        RETURN;
      IFEND;
      v$echo_input_line.column_number := 1;
      IF line_p^.text_kind = dpc$tk_input THEN
        line_p^.text_kind := dpc$tk_input_ready;
        IF window_id = dpv$critical_display_id THEN
          dpp$process_monitor_command (line_p^.text (1, line_p^.text_size));
          line_p^.text_kind := dpc$tk_input;
          line_p^.text_size := 0;
          clean_up_display_queue;
          IF line_p^.next_line_rma = dpc$rma_scd_finished THEN
            queue_line (line_p^);
          IFEND;
        ELSE
          clean_up_display_queue;
          IF line_p^.next_line_rma = dpc$rma_scd_finished THEN
            IF NOT dpv$secure_input_line.secure OR (dpv$secure_input_line.window_id <> window_id) THEN
              queue_line (line_p^);
            IFEND;
          IFEND;
        IFEND;
      ELSE
        next_console_row := v$echo_input_line.row_number MOD dpc$number_of_console_rows + 1;
        v$echo_input_line.text (v$echo_input_line.text_size + 1) := $CHAR (c$acc_carriage_return);
        v$echo_input_line.text_size := v$echo_input_line.text_size + 1;
        IF window_id = v$console_row_mapping [next_console_row].window_id THEN
          v$echo_input_line.row_number := next_console_row;
          v$echo_input_line.text (v$echo_input_line.text_size + 1) := $CHAR (c$acc_line_feed);
          v$echo_input_line.text_size := v$echo_input_line.text_size + 1;
        IFEND;
      IFEND;
      v$echo_input_request.hold_display_for_input := FALSE;
      RETURN;

    = c$acc_cursor_up =
      IF v$echo_input_line.row_number = 1 THEN
        v$echo_input_line.row_number := dpc$number_of_console_rows;
      ELSE
        v$echo_input_line.row_number := v$echo_input_line.row_number - 1;
      IFEND;
      v$echo_input_line.text (v$echo_input_line.text_size + 1) := $CHAR (c$acc_cursor_up);
      v$echo_input_line.text_size := v$echo_input_line.text_size + 1;
      RETURN;

    = c$acc_cursor_right =
      IF v$echo_input_line.column_number = dpc$console_row_size THEN
        v$echo_input_line.column_number := 1;
        v$echo_input_line.row_number := v$echo_input_line.row_number MOD dpc$number_of_console_rows + 1;
      ELSE
        v$echo_input_line.column_number := v$echo_input_line.column_number + 1;
      IFEND;
      v$echo_input_line.text (v$echo_input_line.text_size + 1) := $CHAR (c$acc_cursor_right);
      v$echo_input_line.text_size := v$echo_input_line.text_size + 1;
      RETURN;

    = c$acc_home =
      v$echo_input_line.column_number := 1;
      v$echo_input_line.row_number := 1;
      v$echo_input_line.text (v$echo_input_line.text_size + 1) := $CHAR (c$acc_home);
      v$echo_input_line.text_size := v$echo_input_line.text_size + 1;
      RETURN;

    = c$acc_cursor_down, c$acc_line_feed =
      v$echo_input_line.row_number := v$echo_input_line.row_number MOD dpc$number_of_console_rows + 1;
      v$echo_input_line.text (v$echo_input_line.text_size + 1) := $CHAR (character);
      v$echo_input_line.text_size := v$echo_input_line.text_size + 1;
      RETURN;

    = c$acc_erase =
      IF (v$echo_input_line.column_number > 1) AND (line_p^.text_kind = dpc$tk_input) THEN
        v$echo_input_line.column_number := v$echo_input_line.column_number - 1;
        line_p^.text (v$echo_input_line.column_number) := ' ';
        IF line_p^.text_size < v$echo_input_line.column_number THEN
          line_p^.text_size := v$echo_input_line.column_number;
        IFEND;
        v$echo_input_line.text (v$echo_input_line.text_size + 1) := $CHAR (c$acc_erase);
      ELSE
        v$echo_input_line.text (v$echo_input_line.text_size + 1) := $CHAR (c$acc_bell);
      IFEND;
      v$echo_input_line.text_size := v$echo_input_line.text_size + 1;
      RETURN;

    = c$key_print =
      v$echo_input_line.text (v$echo_input_line.text_size + 1) := $CHAR (c$scd_position_cursor);
      v$echo_input_line.text (v$echo_input_line.text_size + 2) := $CHAR (1 + c$cursor_bias);
      v$echo_input_line.text (v$echo_input_line.text_size + 3) := $CHAR (2 + c$cursor_bias);
      v$echo_input_line.text (v$echo_input_line.text_size + 4) := $CHAR (1e(16));
      v$echo_input_line.text (v$echo_input_line.text_size + 5) := $CHAR (2);
      v$echo_input_line.text_size := v$echo_input_line.text_size + 5;
      v$echo_input_request.hold_display_for_input := FALSE;
      RETURN;

    = c$key_down =
      IF window_p^.kind = dpc$wk_table THEN
        table_starting_line := window_p^.table_starting_line_in_window + v$echo_input_line.row_number -
              window_p^.ending_console_row_number;
        IF table_starting_line < 1 THEN
          table_starting_line := 1;
        ELSEIF table_starting_line > window_p^.table_last_line_used_in_window THEN
          table_starting_line := window_p^.table_last_line_used_in_window;
        IFEND;
        window_p^.table_starting_line_in_window := table_starting_line;
      ELSE
        v$echo_input_line.text (v$echo_input_line.text_size + 1) := $CHAR (c$acc_bell);
        v$echo_input_line.text_size := v$echo_input_line.text_size + 1;
      IFEND;
      v$echo_input_request.hold_display_for_input := FALSE;
      RETURN;

    = c$key_up =
      IF window_p^.kind = dpc$wk_table THEN
        table_starting_line := window_p^.table_starting_line_in_window + v$echo_input_line.row_number -
              window_p^.starting_console_row_number;
        IF table_starting_line < 1 THEN
          table_starting_line := 1;
        ELSEIF table_starting_line > window_p^.table_last_line_used_in_window THEN
          table_starting_line := window_p^.table_last_line_used_in_window;
        IFEND;
        window_p^.table_starting_line_in_window := table_starting_line;
      ELSE
        v$echo_input_line.text (v$echo_input_line.text_size + 1) := $CHAR (c$acc_bell);
        v$echo_input_line.text_size := v$echo_input_line.text_size + 1;
      IFEND;
      v$echo_input_request.hold_display_for_input := FALSE;
      RETURN;

    = c$key_fwd =
      IF window_p^.kind = dpc$wk_table THEN
        lines_in_window := window_p^.ending_console_row_number - window_p^.starting_console_row_number;
        table_starting_line := window_p^.table_starting_line_in_window + lines_in_window;
        IF table_starting_line > (window_p^.table_last_line_used_in_window - lines_in_window DIV 2) THEN
          table_starting_line := window_p^.table_last_line_used_in_window - lines_in_window DIV 2;
        IFEND;
        IF table_starting_line < 1 THEN
          table_starting_line := 1;
        ELSEIF table_starting_line > window_p^.table_last_line_used_in_window THEN
          table_starting_line := window_p^.table_last_line_used_in_window;
        IFEND;
        window_p^.table_starting_line_in_window := table_starting_line;
      ELSE
        v$echo_input_line.text (v$echo_input_line.text_size + 1) := $CHAR (c$acc_bell);
        v$echo_input_line.text_size := v$echo_input_line.text_size + 1;
      IFEND;
      v$echo_input_request.hold_display_for_input := FALSE;
      RETURN;

    = c$key_bkw =
      IF window_p^.kind = dpc$wk_table THEN
        lines_in_window := window_p^.ending_console_row_number - window_p^.starting_console_row_number;
        table_starting_line := window_p^.table_starting_line_in_window - lines_in_window;
        IF table_starting_line < 1 THEN
          table_starting_line := 1;
        ELSEIF table_starting_line > window_p^.table_last_line_used_in_window THEN
          table_starting_line := window_p^.table_last_line_used_in_window;
        IFEND;
        window_p^.table_starting_line_in_window := table_starting_line;
      ELSE
        v$echo_input_line.text (v$echo_input_line.text_size + 1) := $CHAR (c$acc_bell);
        v$echo_input_line.text_size := v$echo_input_line.text_size + 1;
      IFEND;
      v$echo_input_request.hold_display_for_input := FALSE;
      RETURN;

    = c$key_stop =
      IF dpv$enable_stop_key THEN

        { Set the system job monitor system flag to interrupt the current operator command.  The system
        { flag is not set if the dispatcher is in a state such that it can not set the system flag.

        IF window_id = syv$debugger_display_id THEN

          { Set a flag which is used by the debug procedure WRITE_OUTPUT_LINE.

          syv$terminate_sysdebug_output := TRUE;
        ELSE
          IF NOT dpv$lock_main_window.lock OR (dpv$lock_main_window.window_id <> window_id) THEN
            tmp$check_ptl_lock (ptl_interlocked);
            character_processed := NOT ptl_interlocked;
            IF NOT ptl_interlocked THEN
              tmp$set_system_flag (tmv$system_job_monitor_gtid, ofc$operator_break_flag, ignore_status);
            IFEND;
            IF NOT character_processed THEN
              RETURN;
            IFEND;
          IFEND;
        IFEND;
      IFEND;
      v$echo_input_request.hold_display_for_input := FALSE;
      RETURN;

    = c$key_back =
      window_p^.table_starting_line_in_window := 1;
      IF window_p = v$expanded_window_p THEN
        v$expanded_window_p := NIL;
        v$expansions := 0;
        v$re_map_windows := TRUE;
      IFEND;
      IF window_p^.true_class = dpc$wc_pre_empt THEN
        window_p^.class := dpc$wc_pre_empt;
        move_window (window_p);
        v$re_map_windows := TRUE;
      IFEND;
      v$echo_input_request.hold_display_for_input := FALSE;
      RETURN;

    = c$key_shifted_super =
      v$expanded_window_p := window_p;
      v$expansions := 6;
      window_p^.class := dpc$wc_sharing;
      v$re_map_windows := TRUE;
      v$echo_input_request.hold_display_for_input := FALSE;
      RETURN;

    = c$key_f4 =

      { If the F4 function key is typed, return the cursor to the input line of the main operator
      { display window, and simulate the typein of the following command:
      {       DISPLAY_OPERATOR_ACTION_STATUS WAIT=FALSE

      v$echo_input_line.column_number := 1;
      v$echo_input_line.row_number := dpc$number_of_console_rows;
      line_p := v$console_row_mapping [v$echo_input_line.row_number].row_p;
      line_p^.text_kind := dpc$tk_input_ready;
      line_p^.text := 'display_operator_action_status wait=false';
      line_p^.text_size := #SIZE (line_p^.text);
      clean_up_display_queue;
      IF line_p^.next_line_rma = dpc$rma_scd_finished THEN
        queue_line (line_p^);
      IFEND;
      v$echo_input_request.hold_display_for_input := FALSE;
      RETURN;

    = c$key_super =
      IF window_p = v$expanded_window_p THEN
        v$expansions := v$expansions + 1;
      ELSE
        v$expanded_window_p := window_p;
        IF window_p^.class = dpc$wc_pre_empt THEN
          v$expansions := 2;
        ELSE
          v$expansions := 1;
        IFEND;
      IFEND;
      window_p^.class := dpc$wc_sharing;
      v$re_map_windows := TRUE;
      v$echo_input_request.hold_display_for_input := FALSE;
      RETURN;

    = c$key_sub =
      window_p^.class := dpc$wc_sharing;
      IF window_p = v$expanded_window_p THEN
        v$expanded_window_p := NIL;
        v$expansions := 0;
      IFEND;
      v$re_map_windows := TRUE;
      v$echo_input_request.hold_display_for_input := FALSE;
      RETURN;

    ELSE { Unrecognized character, ring the bell.
      v$echo_input_line.text (v$echo_input_line.text_size + 1) := $CHAR (c$acc_bell);
      v$echo_input_line.text_size := v$echo_input_line.text_size + 1;
      RETURN;
    CASEND;

  PROCEND process_keyboard_input;
?? OLDTITLE ??
?? NEWTITLE := 'queue_line', EJECT ??

{ PURPOSE:
{   This procedure adds a line, to display, to the end of the current queue and updates the end of
{   queue pointers.

  PROCEDURE [INLINE] queue_line
    (VAR line: dpt$console_line);

    VAR
      rma: integer;

    line.next_line_p := NIL;
    line.next_line_rma := dpc$rma_end_of_list;

    IF v$start_of_queue_p = NIL THEN
      v$start_of_queue_p := ^line;
    ELSE
      v$end_of_queue_p^.next_line_p := ^line;
      #real_memory_address (#LOC (line), rma);
      v$end_of_queue_p^.next_line_rma := rma DIV 8;
    IFEND;

    v$end_of_queue_p := ^line;

  PROCEND queue_line;
?? OLDTITLE ??
?? NEWTITLE := 'set_console_bell_status', EJECT ??

{ PURPOSE:
{   This procedure sets the console bell status in the SCI parameter table from the system attribute.

  PROCEDURE set_console_bell_status;

    VAR
      console_block_p: ^dpt$console_parameter_block,
      scipt_seq_p: ^SEQ ( * );

    { Set the pointer to the SCI parameter table which is pointed to by word D7RS+2 in the EICB or it is the
    { SSR.  A bit in D7RS tells NOS/VE where the parameter table is located.

    IF mtv$nst_p^.d7rs2.scipt_in_the_ssr THEN
      dsp$mtr_get_ssr_data_seq_ptr (dsc$ssr_sci_parameter_table, scipt_seq_p);
      console_block_p := #ADDRESS (1, #SEGMENT (scipt_seq_p), #OFFSET (scipt_seq_p));
    ELSE
      console_block_p := #ADDRESS (1, syc$msn_cyber_170_cache_bypass, mtv$nst_p^.d7rs2.sci_table * 8);
    IFEND;

    { Interlock the SCI parameter table and set the console bell status.

    set_sci_interlock (console_block_p);
    console_block_p^.scd_console.console_bell := dpv$enable_console_bell;
    console_block_p^.interlocked := FALSE;

  PROCEND set_console_bell_status;
?? OLDTITLE ??
?? NEWTITLE := 'set_sci_interlock', EJECT ??

{ PURPOSE:
{   This procedure sets the interlock to the SCI parameter table.  The caller is responsible for clearing
{   the interlock.

  PROCEDURE set_sci_interlock
    (VAR console_block_p: ^dpt$console_parameter_block);

    CONST
      c$fifteen_milleseconds = 15000;

    VAR
      endtime: ost$free_running_clock,
      previously_set: boolean;

    endtime := #FREE_RUNNING_CLOCK(0) + c$fifteen_milleseconds;
    REPEAT
      i#test_set_bit (^console_block_p^, dpc$sci_table_interlock_bit, previously_set);
      IF NOT previously_set THEN
        RETURN;
      IFEND;
    UNTIL #FREE_RUNNING_CLOCK(0) > endtime;
    mtp$error_stop ('*ERROR* SCI parameter table interlock unavailable.');

  PROCEND set_sci_interlock;
?? OLDTITLE ??
?? NEWTITLE := 'update_critical_messages', EJECT ??

{ PURPOSE:
{   This procedure updates the critical message variable so the message can be logged in the critical log.

  PROCEDURE update_critical_messages (
    message: string(*);
    size: integer);

    VAR
      msg_index: 1..16,
      prev_msg_index: 1..16;

    msg_index := 1;
    While (msg_index < 16) AND (dpv$critical_messages [msg_index].size <> 0) DO
      msg_index := msg_index + 1;
    WHILEND;
    IF msg_index < 16 THEN
      dpv$critical_messages [msg_index].value := message;
      dpv$critical_messages [msg_index].size := size;
    ELSE
      msg_index := 2;
      prev_msg_index := 1;
      WHILE msg_index < 16 DO
        dpv$critical_messages [prev_msg_index].value := dpv$critical_messages [msg_index].value;
        dpv$critical_messages [prev_msg_index].size := dpv$critical_messages [msg_index].size;
        msg_index := msg_index + 1;
        prev_msg_index := prev_msg_index + 1;
      WHILEND;
      dpv$critical_messages [15].value := message;
      dpv$critical_messages [15].size := size;
    IFEND;
    dpv$critical_msgs_need_logging := TRUE;
  PROCEND update_critical_messages;
?? OLDTITLE ??
?? NEWTITLE := 'dpp$change_sci_interrupt_port', EJECT ??

{  PURPOSE:
{    Set the new interrupt port into the SCI parameter table.

  PROCEDURE [XDCL] dpp$change_sci_interrupt_port;

    VAR
      console_block_p: ^dpt$console_parameter_block,
      scipt_seq_p: ^SEQ ( * );

    { Set the pointer to the SCI parameter table which is pointed to by word D7RS+2 in the EICB or it is the
    { SSR.  A bit in D7RS tells NOS/VE where the parameter table is located.

    IF mtv$nst_p^.d7rs2.scipt_in_the_ssr THEN
      dsp$mtr_get_ssr_data_seq_ptr (dsc$ssr_sci_parameter_table, scipt_seq_p);
      console_block_p := #ADDRESS (1, #SEGMENT (scipt_seq_p), #OFFSET (scipt_seq_p));
    ELSE
      console_block_p := #ADDRESS (1, syc$msn_cyber_170_cache_bypass, mtv$nst_p^.d7rs2.sci_table * 8);
    IFEND;

    { Change the interrupt port in the SCI parameter table.

    set_sci_interlock (console_block_p);
    console_block_p^.primary_ve_interface.interrupt_selector := osv$external_interrupt_selector;
    console_block_p^.interlocked := FALSE;

  PROCEND dpp$change_sci_interrupt_port;
?? OLDTITLE ??
?? NEWTITLE := 'dpp$display_error', EJECT ??

*copyc dph$display_error

  PROCEDURE [XDCL] dpp$display_error
    (    message: string ( * <= osc$max_string_size));

    VAR
      actual: integer,
      actual_line_size: 0 .. dpc$console_row_size,
      date_time: ost$date_time,
      date_time_string: dpt$critical_window_date_time,
      id: integer,
      line: string (osc$max_string_size),
      line_index: integer,
      line_p: ^dpt$console_line,
      line_size: 0 .. osc$max_string_size,
      result: 0 .. 2;

    IF dpv$secure_input_line.secure THEN
      RETURN;
    IFEND;

    { Retrieve the date/time to be issued with the message.  The date and time is prefixed to the incoming
    { string.  The time is displayed in the HH:MM:SS format and the date is displayed in the MM/DD/YY format.

    mtv$scb.critical_message_time_stamp := #FREE_RUNNING_CLOCK (0);
    mtp$get_date_time_at_timestamp (mtv$scb.critical_message_time_stamp, date_time);
    date_time_string.string_part := ' ';
    date_time_string.hour (2) := CHR ((date_time.hour MOD 10) + ORD ('0'));
    date_time_string.hour (1) := CHR ((date_time.hour DIV 10) + ORD ('0'));
    date_time_string.colon_1 := ':';
    date_time_string.minute (2) := CHR ((date_time.minute MOD 10) + ORD ('0'));
    date_time_string.minute (1) := CHR ((date_time.minute DIV 10) + ORD ('0'));
    date_time_string.colon_2 := ':';
    date_time_string.second (2) := CHR ((date_time.second MOD 10) + ORD ('0'));
    date_time_string.second (1) := CHR ((date_time.second DIV 10) + ORD ('0'));
    date_time_string.month (2) := CHR ((date_time.month MOD 10) + ORD ('0'));
    date_time_string.month (1) := CHR ((date_time.month DIV 10) + ORD ('0'));
    date_time_string.slash_1 := '/';
    date_time_string.day (2) := CHR ((date_time.day MOD 10) + ORD ('0'));
    date_time_string.day (1) := CHR ((date_time.day DIV 10) + ORD ('0'));
    date_time_string.slash_2 := '/';
    date_time_string.year (2) := CHR ((date_time.year MOD 10) + ORD ('0'));
    date_time_string.year (1) := CHR (((date_time.year MOD 100) DIV 10) + ORD ('0'));
    id := #READ_REGISTER (osc$pr_base_constant);
    actual := 0;
    REPEAT
      #COMPARE_SWAP (dpv$lock, 0, id, actual, result);
    UNTIL (result = 0) OR (actual = id);

    IF (#SIZE (message) = 0) OR (message = ' ') THEN
      line := ' ';
      line_size := 1;
    ELSE

     /trim_message/
      FOR line_index := #SIZE (message) DOWNTO 1 DO
        IF message (line_index) <> ' ' THEN
          line_size := line_index;
          line := message (1, line_size);
          EXIT /trim_message/;
        IFEND;
      FOREND /trim_message/;
    IFEND;

    line_index := 1;
    WHILE line_size > 0 DO
      IF line_size > dpc$critical_window_msg_size THEN
        actual_line_size := dpc$critical_window_msg_size;
        line_size := line_size - dpc$critical_window_msg_size;
      ELSE
        actual_line_size := line_size;
        line_size := 0;
      IFEND;

      dpv$top_window_p^.present_window_line_number :=
            dpv$top_window_p^.present_window_line_number MOD dpc$number_of_console_rows + 1;
      line_p := ^dpv$top_window_p^.lines [dpv$top_window_p^.present_window_line_number];
      line_p^.text := ' ';
      line_p^.text (1, dpc$date_time_size):= date_time_string.string_part;
      line_p^.text ((dpc$date_time_size + 1), *):= line (line_index, actual_line_size);
      line_index := line_index + actual_line_size;

      line_p^.text_size := dpc$date_time_size + actual_line_size;

      IF dpv$scd_block_p^.ve.command = dpc$scd_no_command THEN
        clean_up_display_queue;
      IFEND;

      IF line_p^.next_line_rma = dpc$rma_scd_finished THEN
        line_p^.starting_console_row_number := dpv$top_window_p^.starting_console_row_number;
        line_p^.ending_console_row_number := dpv$top_window_p^.ending_console_row_number;
        queue_line (line_p^);
        update_critical_messages (line_p^.text, line_p^.text_size);
      IFEND;
    WHILEND;

    IF result = 0 THEN
      REPEAT
        #COMPARE_SWAP (dpv$lock, id, 0, actual, result);
      UNTIL result < 2;
      dpp$process_scd_block;
    IFEND;

  PROCEND dpp$display_error;
?? OLDTITLE ??
?? NEWTITLE := 'dpp$display_request', EJECT ??

{ PURPOSE:
{   This procedure is the monitor procedure that is called from system core to request changes to
{   the displays.

  PROCEDURE [XDCL] dpp$display_request
    (VAR rb: dpt$rb_display_request);

    VAR
      actual: integer,
      id: integer,
      index: dpt$number_of_window_lines,
      result: 0 .. 2,
      search_window_p: ^dpt$window,
      work_to_be_done: boolean;

    rb.status.normal := TRUE;
    id := #READ_REGISTER (osc$pr_base_constant);
    REPEAT
      #COMPARE_SWAP (dpv$lock, 0, id, actual, result);
    UNTIL result = 0;
    work_to_be_done := FALSE;

    CASE rb.action OF
    = dpc$da_configure_console =
      configure_console;

    = dpc$da_queue_line =
      IF (rb.line_p^.ending_console_row_number <> 0) AND
            (rb.line_p^.next_line_rma = dpc$rma_scd_finished) THEN
        IF (rb.line_p^.text_kind = dpc$tk_display) AND (rb.window_p^.kind <> dpc$wk_table) THEN
          rb.line_p^.starting_console_row_number := rb.window_p^.starting_console_row_number;
          rb.line_p^.ending_console_row_number := rb.window_p^.ending_console_row_number;
        IFEND;
        queue_line (rb.line_p^);
        work_to_be_done := TRUE;
      IFEND;

    = dpc$da_add_window =
      rb.window_p^.window_id := v$next_window_ordinal;
      v$next_window_ordinal := v$next_window_ordinal + 1;
      IF dpv$top_window_p = NIL THEN
        rb.window_p^.next_window_p := NIL;
        dpv$top_window_p := rb.window_p;
      ELSE
        rb.window_p^.next_window_p := dpv$top_window_p^.next_window_p;
        dpv$top_window_p^.next_window_p := rb.window_p;
      IFEND;
      v$re_map_windows := TRUE;

    = dpc$da_delete_window =
      IF rb.window_p = dpv$top_window_p THEN
        dpv$top_window_p := rb.window_p^.next_window_p;
      ELSE
        search_window_p := dpv$top_window_p;
        WHILE search_window_p^.next_window_p <> rb.window_p DO
          search_window_p := search_window_p^.next_window_p;
        WHILEND;
        search_window_p^.next_window_p := rb.window_p^.next_window_p;
      IFEND;
      IF rb.window_p = v$expanded_window_p THEN
        v$expanded_window_p := NIL;
      IFEND;
      rb.window_p^.next_window_p := v$deleted_windows_p;
      v$deleted_windows_p := rb.window_p;
      v$re_map_windows := TRUE;

    = dpc$da_clear_window =
      FOR index := 1 TO dpc$number_of_window_lines DO
        rb.window_p^.lines [index].text := '  ';
        rb.window_p^.lines [index].text_size := 0;
      FOREND;
      v$re_map_windows := TRUE;

    = dpc$da_change_window =
      IF rb.window_p^.class = dpc$wc_pre_empt THEN
        move_window (rb.window_p);
      IFEND;
      v$re_map_windows := TRUE;

    = dpc$da_set_console_bell_status =
      set_console_bell_status;

    = dpc$da_check_scd_status =
      REPEAT

        { This code is only executed at the end of the BOOT, before transferring control to system core.

        { Wait for SCD to display all of the lines in the display queue.  It is very unlikely that this code
        { would ever have to wait.  This loop has been placed here in case something does happen to SCI and
        { it is unable to process its display queue.  If this case occurs, the system should not continue.
        { The first bit of the field is used by the interrupt handlers to call the display routines when
        { changes occur.  This bit may or may not be set so it is ignored here.

        #SPOIL (dpv$scd_block_p^.scd.current_data_rma);

      UNTIL (dpv$scd_block_p^.scd.current_data_rma MOD 80000000(16)) = 0;

    ELSE
      mtp$set_status_abnormal (dpc$display_processor_id, dpe$invalid_console_monitor_req, rb.status);
    CASEND;

    REPEAT
      #COMPARE_SWAP (dpv$lock, id, 0, actual, result);
    UNTIL result < 2;

    IF work_to_be_done OR v$re_map_windows THEN
      dpp$process_scd_block;
    IFEND;

  PROCEND dpp$display_request;
?? OLDTITLE ??
?? NEWTITLE := 'dpp$process_scd_block', EJECT ??

{ PURPOSE:
{   This procedure processes the system console driver (SCD) communication block.  This procedure is called
{   to process SCD external interrupts and on a periodic basis is called from the normal monitor loop.
{
{ NOTE:
{   This procedure can be called from the monitor interrupt handler which means that an interruptable monitor
{   process may have been interrupted.  Exercise caution when calling procedures outside of this module as
{   they may not be in a state to handle the request properly, this is especially true if the procedure
{   called is from the interrupted process.

  PROCEDURE [XDCL] dpp$process_scd_block;

    VAR
      actual: integer,
      id: integer,
      index: 1 .. 3,
      character_processed: boolean,
      old_traps: 0 .. 3,
      result: 0 .. 2,
      rma: integer,
      scd_data_for_ve: dpt$scd_data_for_ve,
      work_to_be_done: boolean;

    { Disable traps, this process can not be interrupted.

    i#mtr_disable_traps (old_traps);

    REPEAT
      id := #READ_REGISTER (osc$pr_base_constant);
      actual := 0;
      REPEAT
        #COMPARE_SWAP (dpv$lock, 0, id, actual, result);
      UNTIL result < 2;

      IF result = 1 THEN
        i#mtr_restore_traps (old_traps);
        RETURN;
      IFEND;

      work_to_be_done := FALSE;

      IF dpv$scd_block_p^.ve.command = dpc$scd_no_command THEN

        IF dpv$scd_block_p^.scd.id <> v$previous_scd_data_id THEN

          { Process the keyboard input from SCD.

          #real_memory_address (#LOC (v$echo_input_line), rma);
          v$echo_input_request.console_data_rma := rma DIV 8;

          scd_data_for_ve := dpv$scd_block_p^.scd;
          v$echo_input_line.next_line_rma := dpc$rma_end_of_list;
          v$echo_input_line.text_size := 0;

        /process_console_input/
          FOR index := 1 TO 3 DO
            IF scd_data_for_ve.input_buffer [index] <> 0 THEN
              process_keyboard_input (scd_data_for_ve.input_buffer [index], character_processed);
              IF NOT character_processed THEN
                EXIT /process_console_input/;
              IFEND;
            IFEND;
          FOREND /process_console_input/;

          IF character_processed THEN

            { Save a copy of the processed data to keep track of what was processed this pass and check if
            { anything to process next pass.

            v$previous_scd_data_id := scd_data_for_ve.id;

            { Echo the processed characters to keyboard and set time for next periodic call.

            dpv$scd_block_p^.ve := v$echo_input_request;
            v$end_pause := #FREE_RUNNING_CLOCK (0) + 10 * 1000000;
          IFEND;

        ELSEIF v$echo_input_request.hold_display_for_input THEN
          IF v$end_pause < #FREE_RUNNING_CLOCK (0) THEN
            v$echo_input_request.hold_display_for_input := FALSE;
            dpv$scd_block_p^.ve.command := dpc$scd_resume_output;
          IFEND;

        ELSEIF v$re_map_windows THEN
          map_windows_onto_console;
          v$re_map_windows := FALSE;
          work_to_be_done := TRUE;

        ELSE
          clean_up_display_queue;
          IF (v$scd_queue_p = NIL) AND (v$start_of_queue_p <> NIL) THEN
            v$scd_queue_p := v$start_of_queue_p;
            v$start_of_queue_p := NIL;
            v$end_of_queue_p^.next_line_p := NIL;
            v$end_of_queue_p^.next_line_rma := dpc$rma_end_of_list;

            { Set the display queue information in the SCD communications block for the new queue.  The
            { command to SCD must be set after all other fields are set.

            #real_memory_address (v$scd_queue_p, rma);
            dpv$scd_block_p^.ve.console_data_rma := rma DIV 8;
            v$last_scd_rma := rma DIV 8;
            #SPOIL (dpv$scd_block_p^.ve);
            dpv$scd_block_p^.ve.command := dpc$scd_begin_new_output_list;
            mtv$nst_p^.d8st.operator_action := dpv$180_operator_action;
          IFEND;
        IFEND;
      IFEND;

      IF v$echo_input_request.hold_display_for_input THEN
        dpv$scd_time := v$end_pause;
      ELSE
        dpv$scd_time := #FREE_RUNNING_CLOCK (0) + 5 * 1000000;
      IFEND;

      REPEAT
        #COMPARE_SWAP (dpv$lock, id, 0, actual, result);
      UNTIL result < 2;

    UNTIL NOT work_to_be_done;
    i#mtr_restore_traps (old_traps);

  PROCEND dpp$process_scd_block;
MODEND dpm$system_console_monitor;
