?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Deadstart : Boot' ??
MODULE dsm$boot;

{ PURPOSE:
{   This module contains all of the procedures needed to load the boot pieces from the deadstart device.
{   After the boot pieces (monitor image and system core image) are loaded, this module contains the
{   the procedure which calls DFT to change the monitor exchange package from the boot to system core.
{   This is the last code in the boot to be executed.

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc bat$record_header_type
*copyc dsc$max_dcfile_length
*copyc dst$dcfile_identifier
*copyc dpt$rb_display_request
*copyc mmt$segment_descriptor_table
*copyc mtc$job_fixed_segment
*copyc osc$purge_map_and_cache
*copyc ost$exchange_package
*copyc ost$hardware_subranges
*copyc pmt$virtual_memory_image_header
*copyc syc$monitor_segment_numbers
?? POP ??
*copyc cmp$configure_deadstart_device
*copyc cmp$de_configure_ds_device
*copyc cmp$vcmb_menu_manager
*copyc cmp$write_os_status
*copyc dsp$change_monitor_xp
*copyc dsp$prepare_deadstart_io
*copyc dsp$read_deadstart_device
*copyc dsp$read_header_labels
*copyc dsp$save_boot_data_pointer
*copyc dsp$store_data_in_ssr
*copyc i#build_adaptable_seq_pointer
*copyc i#call_monitor
*copyc i#real_memory_address
*copyc mmp$boot_add_sdt_sdtx_entry
*copyc mmp$get_max_sdt_pointer
*copyc osp$system_error
*copyc pmp$zero_out_table
*copyc syp$display_deadstart_message
*copyc syp$trace_deadstart_message
?? EJECT ??
*copyc cmv$system_device_data
*copyc jmv$jmtr_xcb
*copyc mmv$pt_length
*copyc mtv$monitor_segment_table
*copyc mtv$nst_p
*copyc osv$mainframe_wired_heap
*copyc osv$page_size
?? POP ??
?? TITLE := 'Global Declarations Declared by This Module', EJECT ??
  TYPE
    asid_list_entry = RECORD
      asid: ost$asid,
      link_p: ^asid_list_entry,
    RECEND,

    segment_list_entry = RECORD
      linked_segment_description: pmt$linked_segment_description,
      link_p: ^segment_list_entry,
    RECEND,

    segment_table_entry = PACKED RECORD
      ste: ost$segment_descriptor,
      fill: 0 .. 0ffffff(16),
    RECEND;
?? EJECT ??
  VAR
    dsv$dcfile_identifier: [XDCL] dst$dcfile_identifier,
    dsv$display_deadstart_messages: [XDCL] boolean := TRUE,
    dsv$maximum_memory_size: [XDCL] integer := 0,

    asid_seed: integer := 1,
    core_sta_p: ^SEQ ( * ),
    current_asid_entry_p: ^asid_list_entry := NIL,
    current_core_segment_entry_p: ^segment_list_entry := NIL,
    current_monitor_segment_entry_p: ^segment_list_entry := NIL,
    job_fixed_asid: ost$asid,
    monitor_sta_p: ^SEQ ( * ),
    monitor_xp_p: ^ost$exchange_package,
    sdte: mmt$segment_descriptor := [[osc$vl_regular_segment, osc$non_executable, osc$read_uncontrolled,
          osc$write_uncontrolled, 1, 1, 0, [FALSE, FALSE, 0]], 0, 0],
    top_asid_entry_p: ^asid_list_entry := NIL,
    top_core_segment_entry_p: ^segment_list_entry := NIL,
    top_monitor_segment_entry_p: ^segment_list_entry := NIL;
?? TITLE := 'build_asid', EJECT ??

{ PURPOSE:
{   This procedure assigns an asid.  The algorithm attempts to distribute ASIDS as far apart in the page
{   table as possible.  It checks that the asid does not already exists on the asid list.

  PROCEDURE build_asid
    (VAR asid: ost$asid);

    CONST
      nos_asid = 0ffff(16);

    VAR
      asid_entry_p: ^asid_list_entry,
      asid_found: boolean,
      asid_index: integer,
      page_table_length: integer,
      ptl_index: integer;

    asid_found := TRUE;
    page_table_length := mmv$pt_length * 8;

    REPEAT
      asid_index := asid_seed;
      asid_seed := asid_seed + 1;

      { Determine value to mirror the ASID seed based on the page table size.

      ptl_index := page_table_length DIV 32;
      ptl_index := (((page_table_length DIV 32) - 1) MOD 8000(16)) + 1;
      asid := 0;

      {  Generate the ASID, this creates a mirror image of the initial ASID seed.

      WHILE asid_index <> 0 DO
        IF (asid_index MOD 2) <> 0 THEN
          asid := asid + ptl_index;
        IFEND;
        asid_index := asid_index DIV 2;
        ptl_index := ptl_index DIV 2;
      WHILEND;

      IF (asid <> nos_asid) THEN
        asid_found := FALSE;
        asid_entry_p := top_asid_entry_p^.link_p;

       /search_asid_list/
        WHILE asid_entry_p <> NIL DO
          IF asid_entry_p^.asid = asid THEN
            asid_found := TRUE;
            EXIT /search_asid_list/;
          IFEND;
          asid_entry_p := asid_entry_p^.link_p;
        WHILEND /search_asid_list/;
      IFEND;

    UNTIL NOT asid_found;

  PROCEND build_asid;
?? TITLE := 'define_sta', EJECT ??

{ PURPOSE:
{   This procedure defines the STA (system table address) pointer for the given XP pointer.

  PROCEDURE define_sta
    (VAR xp_p: ^ost$exchange_package;
     VAR sta_p: ^SEQ ( * ));

    { Assumes that sta1/sta2 is a relative offset into xp/st segment.

    i#build_adaptable_seq_pointer (#RING (xp_p), #SEGMENT (xp_p),
          (10000(16) * xp_p^.segment_table_address_1) + xp_p^.segment_table_address_2,
          (xp_p^.segment_table_length + 1) * 8, 0, sta_p);

  PROCEND define_sta;
?? TITLE := 'find_segment', EJECT ??

{ PURPOSE:
{   This procedure searches the currect segment list for the desired segment.

  PROCEDURE find_segment
    (    segment_list_p: ^segment_list_entry;
         segment_number: ost$segment;
     VAR segment_entry: segment_list_entry);

    VAR
      segment_entry_p: ^segment_list_entry;

    segment_entry_p := segment_list_p^.link_p;

    WHILE segment_entry_p <> NIL DO
      IF segment_entry_p^.linked_segment_description.segment_number = segment_number THEN
        segment_entry := segment_entry_p^;
        RETURN;
      IFEND;
      segment_entry_p := segment_entry_p^.link_p;
    WHILEND;

    osp$system_error ('Unable to find desired segment in the segment entry list.', NIL);

  PROCEND find_segment;
?? TITLE := 'initiate_deadstart', EJECT ??

{ PURPOSE:
{   This procedure performs the final initialization of system core environment and then calls
{   DFT to start the CPU in the newly defined monitor address space.
{ NOTES:
{   There is no return from the call to this procedure, execution begins with the monitor
{   exchange package in system core.

  PROCEDURE initiate_deadstart;

    VAR
      boot_asids: [STATIC] dst$boot_asids := [0, 0, 0, 0],
      buffer_p: ^cell,
      core_sdt_p: ^mmt$segment_descriptor_table,
      display_rb: dpt$rb_display_request,
      monitor_sdt_p: ^mmt$segment_descriptor_table,
      mps: integer,
      status: ost$status,
      ste_p: ^integer;

    { Perform functions of real memory builder - define_segment, share_segment, extend_segment
    { HELP!!! How to do extend?????????????

    boot_asids.code_data := mtv$monitor_segment_table.st [0a(16)].ste.asid;
    boot_asids.job_stack := mtv$monitor_segment_table.st [0c(16)].ste.asid;
    boot_asids.mtr_stack := mtv$monitor_segment_table.st [0b(16)].ste.asid;
    boot_asids.spare := 0;
    dsp$save_boot_data_pointer (dsc$boot_asids, #SEQ (boot_asids));

    { Move some segment descriptor entries from the monitor boot SDT to the monitor SDT of the system
    { core.  Some segments in the system job monitor of system core are initialized from monitor's SDT.

    RESET core_sta_p;
    RESET monitor_sta_p;
    NEXT core_sdt_p: [0 .. 63] IN core_sta_p;
    NEXT monitor_sdt_p: [0 .. 63] IN monitor_sta_p;

    monitor_sdt_p^.st [syc$msn_page_table] := mtv$monitor_segment_table.st [syc$msn_page_table];
    monitor_sdt_p^.st [syc$msn_cyber_170_cache_bypass] :=
          mtv$monitor_segment_table.st [syc$msn_cyber_170_cache_bypass];
    monitor_sdt_p^.st [syc$msn_cyber_170] := mtv$monitor_segment_table.st [syc$msn_cyber_170];
    monitor_sdt_p^.st [syc$msn_system_status_record] :=
          mtv$monitor_segment_table.st [syc$msn_system_status_record];
    core_sdt_p^.st [syc$msn_page_table] := monitor_sdt_p^.st [syc$msn_page_table];
    core_sdt_p^.st [osc$segnum_mainframe_wired] := monitor_sdt_p^.st [syc$msn_mainframe_wired];
    core_sdt_p^.st [osc$segnum_mainframe_wired_cb] := monitor_sdt_p^.st [syc$msn_mainframe_wired_cb];
    core_sdt_p^.st [syc$msn_network_wired] := monitor_sdt_p^.st [syc$msn_network_wired];
    monitor_sdt_p^.st [mtc$job_fixed_segment] := core_sdt_p^.st [osc$segnum_job_fixed_heap];

    { Provide pointer to the EICB and the SDTE of the NOS exchange package.

    ste_p := #LOC (mtv$monitor_segment_table.st [#SEGMENT (osv$mainframe_wired_heap)]);
    monitor_xp_p^.x_registers [13] := ste_p^;
    monitor_xp_p^.x_registers [15] := #OFFSET (mtv$nst_p);
    i#real_memory_address (monitor_xp_p, mps);

    buffer_p := NIL;
    #PURGE_BUFFER (osc$purge_all_page_seg_map, buffer_p);
    #PURGE_BUFFER (osc$purge_all_cache, buffer_p);
    syp$display_deadstart_message ('Deadstart NOS/VE ...');

    { Check that SCD has finished displaying all data in the display queue.

    display_rb.reqcode := syc$rc_update_system_display;
    display_rb.action := dpc$da_check_scd_status;
    i#call_monitor (#LOC (display_rb), #SIZE (display_rb));

    dsp$change_monitor_xp (dsc$dft_select_first_active_cpu, mps, status);
    osp$system_error ('Deadstart cpu.', ^status);

  PROCEND initiate_deadstart;
?? TITLE := 'load_dcfile', EJECT ??

{ PURPOSE:
{   This procedure loads the DCFILE from the deadstart device.  The DCFILE must be loaded in the boot
{   because it is needed in system core before the page frame table is initialized and the deadstart
{   device can not be read until the page frame table is initialized.

  PROCEDURE load_dcfile;

    VAR
      bam_record_header_seq_p: ^SEQ ( * ),
      bam_record_header: bat$record_header,
      data_size_read: integer,
      dcfile_bam_header_p: ^bat$record_header,
      dcfile_found: boolean,
      dcfile_identifier_p: ^dst$dcfile_identifier,
      dcfile_length_p: ^0 .. dsc$max_dcfile_length,
      dcfile_line_p: ^SEQ ( * ),
      dcfile_seq_p: ^SEQ ( * ),
      device_line_p: ^SEQ ( * ),
      device_line_seq_p: ^SEQ ( * ),
      file_identifier: dst$deadstart_file_identifier;

    dsp$read_header_labels (file_identifier);
    IF file_identifier <> 'DCFILE' THEN
      osp$system_error ('Invalid deadstart file: Cannot find DCFILE.', NIL);
    IFEND;

    { Search for the correct DCFILE.

    bam_record_header_seq_p := #SEQ (bam_record_header);
    PUSH device_line_seq_p: [[REP osc$max_string_size OF cell]];
    RESET device_line_seq_p;
    dcfile_found := FALSE;

   /search_for_dcfile/
    WHILE TRUE DO
      dsp$read_deadstart_device (#SIZE (bam_record_header_seq_p^), bam_record_header_seq_p, data_size_read);
      IF data_size_read < #SIZE (bam_record_header_seq_p^) THEN
        EXIT /search_for_dcfile/;
      IFEND;
      IF bam_record_header.length > 0 THEN
        dsp$read_deadstart_device (bam_record_header.length, device_line_seq_p, data_size_read);
        IF data_size_read < bam_record_header.length THEN
          EXIT /search_for_dcfile/;
        IFEND;
        IF bam_record_header.length >= #SIZE (dst$dcfile_identifier) THEN
          NEXT dcfile_identifier_p IN device_line_seq_p;
          IF dcfile_identifier_p^ = dsv$dcfile_identifier THEN
            dcfile_found := TRUE;
            EXIT /search_for_dcfile/;
          IFEND;
        IFEND;
      IFEND;
    WHILEND /search_for_dcfile/;

    ALLOCATE dcfile_seq_p: [[REP dsc$max_dcfile_length OF cell]] IN osv$mainframe_wired_heap^;
    RESET dcfile_seq_p;
    dsp$save_boot_data_pointer (dsc$dcfile_data, dcfile_seq_p);
    NEXT dcfile_length_p IN dcfile_seq_p;
    dcfile_length_p^ := 0;

    IF NOT dcfile_found THEN
      RETURN;
    IFEND;

    WHILE TRUE DO
      dsp$read_deadstart_device (#SIZE (bam_record_header_seq_p^), bam_record_header_seq_p, data_size_read);
      IF data_size_read < #SIZE (bam_record_header_seq_p^) THEN
        RETURN;
      IFEND;
      IF bam_record_header.length > 0 THEN
        dsp$read_deadstart_device (bam_record_header.length, device_line_seq_p, data_size_read);
        IF data_size_read < bam_record_header.length THEN
          RETURN;
        IFEND;
        IF bam_record_header.length >= #SIZE (dst$dcfile_identifier) THEN
          NEXT dcfile_identifier_p IN device_line_seq_p;
          IF dcfile_identifier_p^ (1, 3) = dsv$dcfile_identifier (1, 3) THEN
            RETURN;
          IFEND;
          RESET device_line_seq_p;
        IFEND;

        NEXT dcfile_bam_header_p IN dcfile_seq_p;
        dcfile_length_p^ := dcfile_length_p^ + #SIZE (dcfile_bam_header_p^);
        dcfile_bam_header_p^ := bam_record_header;

        NEXT device_line_p: [[REP bam_record_header.length OF cell]] IN device_line_seq_p;
        RESET device_line_p;

        NEXT dcfile_line_p: [[REP bam_record_header.length OF cell]] IN dcfile_seq_p;
        dcfile_length_p^ := dcfile_length_p^ + #SIZE (dcfile_line_p^);
        RESET dcfile_line_p;

        dcfile_line_p^ := device_line_p^;
      IFEND;
    WHILEND;

  PROCEND load_dcfile;
?? TITLE := 'load_monitor_image', EJECT ??

{ PURPOSE:
{   This procedure loads the monitor image from the deadstart device.

  PROCEDURE load_monitor_image;

    CONST
      byte_limit = 8000000(16);

    VAR
      asid: ost$asid,
      boot_page_count: integer,
      boot_page_size: integer,
      boot_segment: ost$segment,
      data_p: ^SEQ ( * ),
      data_size_read: integer,
      end_rma: integer,
      job_fixed_segment_number: ost$segment,
      memory_bounds: dst$ssr_boot_memory_bounds,
      memory_image_header: pmt$virtual_memory_image_header,
      memory_image_header_seq_p: ^SEQ ( * ),
      message: string (80),
      message_length: integer,
      page_boundary_segment_length: integer,
      remainder: integer,
      rma: integer,
      sdt_p: mmt$max_sdt_p,
      sdtxe: mmt$segment_descriptor_extended,
      segment_b_and_c_index: 1 .. 2,
      segment_entry: segment_list_entry,
      segment_header: pmt$linked_segment_description,
      segment_header_seq_p: ^SEQ ( * ),
      segment_index: ost$segment,
      segment_length: ost$segment_length,
      segment_number: ost$segment,
      start_rma: integer,
      status: ost$status,
      sva: ost$system_virtual_address,
      sva_count: integer,
      sva_found: boolean,
      sva_index: integer,
      temp_pva_p: ^cell;

    { The monitor image's header labels are not read here because they were read as part of
    { the dsp$prepare_deadstart_io code.

    { Read monitor image's memory image header from the deadstart device.

    memory_image_header_seq_p := #SEQ (memory_image_header);
    dsp$read_deadstart_device (#SIZE (pmt$virtual_memory_image_header), memory_image_header_seq_p,
          data_size_read);
    IF data_size_read < #SIZE (pmt$virtual_memory_image_header) THEN
      osp$system_error ('Invalid monitor memory image header.', NIL);
    IFEND;

    segment_header_seq_p := #SEQ (segment_header);
    segment_number := 0;
    job_fixed_segment_number := 0;
    FOR segment_index := 1 TO memory_image_header.number_of_segments DO

      { Read monitor image's segment header.

      RESET segment_header_seq_p;
      dsp$read_deadstart_device (#SIZE (pmt$linked_segment_description), segment_header_seq_p,
            data_size_read);
      IF data_size_read < #SIZE (pmt$linked_segment_description) THEN
        osp$system_error ('Invalid monitor segment header.', NIL);
      IFEND;

      { Build an ASID and add it to the SDT.

      build_asid (asid);
      sdte.ste.asid := asid;
      mmp$boot_add_sdt_sdtx_entry (sdte, sdtxe, segment_number);

      STRINGREP (message, message_length, '  loading segment ', segment_header.segment_number: 4: #(16),
            asid: 6: #(16), segment_header.length: 20);
      syp$trace_deadstart_message (message (1, message_length));

      { Pad the monitor code segment to enable recovery process capability of reload in monitor code space.

      IF segment_header.segment_number = #SEGMENT (memory_image_header.starting_procedure.code_pva) THEN

        { Build a job fixed ASID and add it to the SDT.

        build_asid (job_fixed_asid);
        sdte.ste.asid := job_fixed_asid;
        mmp$boot_add_sdt_sdtx_entry (sdte, sdtxe, job_fixed_segment_number);

        { Count the number of pages assigned to the boot.  The boot has three segments (A, B and C).

        boot_page_count := 0;
        boot_segment := #SEGMENT (osv$mainframe_wired_heap);
        mmp$get_max_sdt_pointer (^jmv$jmtr_xcb, sdt_p);
        sva.asid := sdt_p^.st [boot_segment].ste.asid;
        sva.offset := 0;
        WHILE (sva.offset < byte_limit) DO
          sva_found := FALSE;
          #HASH_SVA (sva, sva_index, sva_count, sva_found);
          IF sva_found THEN
            boot_page_count := boot_page_count + 1;
          IFEND;
          sva.offset := sva.offset + osv$page_size;
        WHILEND;
        FOR segment_b_and_c_index := 1 TO 2 DO { find # of pages in segments B and C }
          boot_segment := boot_segment + 1;
          sva.asid := sdt_p^.st [boot_segment].ste.asid;
          sva.offset := 0;
          sva_found := FALSE;
          REPEAT
            #HASH_SVA (sva, sva_index, sva_count, sva_found);
            IF sva_found THEN
              boot_page_count := boot_page_count + 1;
            IFEND;
            sva.offset := sva.offset + osv$page_size;
          UNTIL NOT sva_found;
        FOREND;
        boot_page_count := boot_page_count + 20;
        boot_page_size := (boot_page_count * osv$page_size);

        { Assign real memory to monitor code segment.

        temp_pva_p := #ADDRESS (1, segment_number, 0);
        pmp$zero_out_table (temp_pva_p, segment_header.length);

        { Round the segment length to a page boundary.

        page_boundary_segment_length := ((segment_header.length + osv$page_size - 1) DIV
              osv$page_size) * osv$page_size;

        { Add the remainder of pages to the system job's job fixed segment.

        remainder := boot_page_size - page_boundary_segment_length;
        temp_pva_p := #ADDRESS (1, job_fixed_segment_number, 0);
        pmp$zero_out_table (temp_pva_p, remainder);

        { Determine if the memory obtained is sequential.

        i#real_memory_address (#ADDRESS (1, segment_number, 0), start_rma);
        i#real_memory_address (#ADDRESS (1, job_fixed_segment_number, remainder - 1), end_rma);
        IF (end_rma - start_rma + 1) <> boot_page_size THEN
          osp$system_error ('Did not obtain sequential memory for monitor image.', NIL);
        IFEND;

        { Save the memory bounds in the SSR.  The RMA is converted to units of 100 octal words.

        rma := start_rma DIV 1000(8);
        memory_bounds.start_address.r_lower := rma MOD 10000(8);
        memory_bounds.start_address.r_upper := rma DIV 10000(8);
        rma := (end_rma - start_rma + 1) DIV 1000(8);
        memory_bounds.length.r_lower := rma MOD 10000(8);
        memory_bounds.length.r_upper := rma DIV 10000(8);
        dsp$store_data_in_ssr (dsc$ssr_boot_memory_bounds, #SEQ (memory_bounds));
      IFEND;

      { Read the monitor image's segment from the deadstart device.

      i#build_adaptable_seq_pointer (1, segment_number, 0, segment_header.length, 0, data_p);
      dsp$read_deadstart_device (segment_header.length, data_p, data_size_read);
      IF data_size_read < segment_header.length THEN
        osp$system_error ('Unable to read a segment from the deadstart device.', NIL);
      IFEND;
      save_segment_entry (asid, segment_header, current_monitor_segment_entry_p);
      save_asid_entry (asid);
    FOREND;

    { Find the monitor exchange package segment.  Add the ASID to the SDT.

    find_segment (top_monitor_segment_entry_p, #SEGMENT (memory_image_header.exchange_package),
          segment_entry);
    sdte.ste.asid := segment_entry.linked_segment_description.segment_descriptor.asid;
    mmp$boot_add_sdt_sdtx_entry (sdte, sdtxe, segment_number);

    { Retrieve a pointer to the monitor exchange package.

    monitor_xp_p := #ADDRESS (1, segment_number, #OFFSET (memory_image_header.exchange_package));
    define_sta (monitor_xp_p, monitor_sta_p);

    monitor_xp_p^.p_register.pva.seg := #SEGMENT (memory_image_header.starting_procedure.code_pva);
    monitor_xp_p^.p_register.pva.offset := #OFFSET (memory_image_header.starting_procedure.code_pva);
    monitor_xp_p^.a3 := memory_image_header.starting_procedure.binding_pva;

    i#real_memory_address (monitor_sta_p, rma);
    monitor_xp_p^.segment_table_address_1 := rma DIV 10000(16);
    monitor_xp_p^.segment_table_address_2 := rma MOD 10000(16);

    move_stes_to_segment_table (top_monitor_segment_entry_p, monitor_xp_p, monitor_sta_p, status);

  PROCEND load_monitor_image;
?? TITLE := 'load_system_core_image', EJECT ??

{ PURPOSE:
{   This procedure loads the system core image from the deadstart device.

  PROCEDURE load_system_core_image;

    VAR
      asid: ost$asid,
      core_xp_p: ^ost$exchange_package,
      data_p: ^SEQ ( * ),
      data_size_read: integer,
      file_identifier: dst$deadstart_file_identifier,
      memory_image_header: pmt$virtual_memory_image_header,
      memory_image_header_seq_p: ^SEQ ( * ),
      message: string (80),
      message_length: integer,
      ring_index: 1 .. 3,
      rma: integer,
      sdtxe: mmt$segment_descriptor_extended,
      segment_entry: segment_list_entry,
      segment_header: pmt$linked_segment_description,
      segment_header_seq_p: ^SEQ ( * ),
      segment_index: ost$segment,
      segment_length: ost$segment_length,
      segment_number: ost$segment,
      status: ost$status,
      stp_p: ^mmt$segment_descriptor_table;

    dsp$read_header_labels (file_identifier);
    IF file_identifier <> 'SYSTEM_CORE_IMAGE' THEN
      osp$system_error ('Invalid deadstart file: Cannot find SYSTEM_CORE_IMAGE.', NIL);
    IFEND;

    { Read system core image's memory image header from the deadstart device.

    memory_image_header_seq_p := #SEQ (memory_image_header);
    dsp$read_deadstart_device (#SIZE (pmt$virtual_memory_image_header), memory_image_header_seq_p,
          data_size_read);
    IF data_size_read < #SIZE (pmt$virtual_memory_image_header) THEN
      osp$system_error ('Invalid system core memory image header.', NIL);
    IFEND;

    segment_header_seq_p := #SEQ (segment_header);
    segment_number := 0;
    FOR segment_index := 1 TO memory_image_header.number_of_segments DO

      { Read system core image's segment header.

      RESET segment_header_seq_p;
      dsp$read_deadstart_device (#SIZE (pmt$linked_segment_description), segment_header_seq_p,
            data_size_read);
      IF data_size_read < #SIZE (pmt$linked_segment_description) THEN
        osp$system_error ('Invalid monitor segment header.', NIL);
      IFEND;

      { Build an ASID and add it to the SDT.

      IF segment_header.segment_number = #SEGMENT (memory_image_header.exchange_package) THEN
        asid := job_fixed_asid;
      ELSE
        build_asid (asid);
      IFEND;
      sdte.ste.asid := asid;
      mmp$boot_add_sdt_sdtx_entry (sdte, sdtxe, segment_number);

      STRINGREP (message, message_length, '  loading segment ', segment_header.segment_number: 4: #(16),
            asid: 6: #(16), segment_header.length: 20);
      syp$trace_deadstart_message (message (1, message_length));

      { Read the system core image's segment from the deadstart device.

      i#build_adaptable_seq_pointer (1, segment_number, 0, segment_header.length, 0, data_p);
      dsp$read_deadstart_device (segment_header.length, data_p, data_size_read);
      IF data_size_read < segment_header.length THEN
        osp$system_error ('Unable to read a segment from the deadstart device.', NIL);
      IFEND;
      save_segment_entry (asid, segment_header, current_core_segment_entry_p);
      save_asid_entry (asid);
    FOREND;

    { Find the system core exchange package segment.  Add the ASID to the SDT.

    find_segment (top_core_segment_entry_p, #SEGMENT (memory_image_header.exchange_package),
          segment_entry);
    sdte.ste.asid := segment_entry.linked_segment_description.segment_descriptor.asid;
    mmp$boot_add_sdt_sdtx_entry (sdte, sdtxe, segment_number);

    { Retrieve a pointer to the system core exchange package.

    core_xp_p := #ADDRESS (1, segment_number, #OFFSET (memory_image_header.exchange_package));
    define_sta (core_xp_p, core_sta_p);

    core_xp_p^.p_register.pva.ring := #RING (memory_image_header.starting_procedure.code_pva);
    core_xp_p^.p_register.pva.seg := #SEGMENT (memory_image_header.starting_procedure.code_pva);
    core_xp_p^.p_register.pva.offset := #OFFSET (memory_image_header.starting_procedure.code_pva);
    core_xp_p^.a3 := memory_image_header.starting_procedure.binding_pva;

    i#real_memory_address (core_sta_p, rma);
    core_xp_p^.segment_table_address_1 := rma DIV 10000(16);
    core_xp_p^.segment_table_address_2 := rma MOD 10000(16);

    move_stes_to_segment_table (top_core_segment_entry_p, core_xp_p, core_sta_p, status);

    RESET core_sta_p;
    NEXT stp_p: [0 .. core_xp_p^.segment_table_length] IN core_sta_p;
    FOR ring_index := 1 TO 3 DO
      core_xp_p^.tos_registers [ring_index].pva.ring := ring_index;
      stp_p^.st [core_xp_p^.tos_registers [ring_index].pva.seg].ste.r1 := ring_index;
      stp_p^.st [core_xp_p^.tos_registers [ring_index].pva.seg].ste.r2 := ring_index;
    FOREND;

  PROCEND load_system_core_image;
?? TITLE := 'move_stes_to_segment_table', EJECT ??

  PROCEDURE move_stes_to_segment_table
    (    segment_list_p: ^segment_list_entry;
     VAR xp_p: ^ost$exchange_package;
     VAR segment_table_p: ^SEQ ( * );
     VAR status: ost$status);

    VAR
      entry: segment_table_entry,
      segment_entry_p: ^segment_list_entry,
      ste_p: ^ARRAY [0 .. * ] OF segment_table_entry;

    RESET segment_table_p;
    NEXT ste_p: [0 .. xp_p^.segment_table_length] IN segment_table_p;

    entry.fill := 0;
    segment_entry_p := segment_list_p^.link_p;
    WHILE segment_entry_p <> NIL DO
      entry.ste := segment_entry_p^.linked_segment_description.segment_descriptor;
      ste_p^ [segment_entry_p^.linked_segment_description.segment_number] := entry;
      segment_entry_p := segment_entry_p^.link_p;
    WHILEND;

  PROCEND move_stes_to_segment_table;
?? TITLE := 'save_asid_entry', EJECT ??

{ PURPOSE:
{   This procedure allocates an asid entry, saves the asid in the entry and adds the entry to the asid list.

  PROCEDURE save_asid_entry
    (    asid: ost$asid);

    VAR
      asid_entry_p: ^asid_list_entry;

    ALLOCATE asid_entry_p IN osv$mainframe_wired_heap^;
    asid_entry_p^.asid := asid;
    asid_entry_p^.link_p := NIL;

    current_asid_entry_p^.link_p := asid_entry_p;
    current_asid_entry_p := current_asid_entry_p^.link_p;

  PROCEND save_asid_entry;
?? TITLE := 'save_segment_entry', EJECT ??

{ PURPOSE:
{   This procedure allocates a segment entry, saves the segment information in the entry and
{   adds the entry to the segment list.

  PROCEDURE save_segment_entry
    (    asid: ost$asid;
         segment_descriptor: pmt$linked_segment_description;
     VAR current_segment_entry_p: ^segment_list_entry);

    VAR
      segment_entry_p: ^segment_list_entry;

    ALLOCATE segment_entry_p IN osv$mainframe_wired_heap^;
    segment_entry_p^.linked_segment_description := segment_descriptor;
    segment_entry_p^.linked_segment_description.segment_descriptor.asid := asid;
    segment_entry_p^.link_p := NIL;

    current_segment_entry_p^.link_p := segment_entry_p;
    current_segment_entry_p := current_segment_entry_p^.link_p;

  PROCEND save_segment_entry;
?? TITLE := 'dsp$boot_deadstart_loader', EJECT ??

{ PURPOSE:
{   This procedure determines whether the deadstart device contains a valid deadstart file and
{   loads the memory image and the system core image from the device.  If there are no fatal errors
{   during the loading of these files, control should change to the system core after this procedure
{   has completed.

  PROCEDURE [XDCL] dsp$boot_deadstart_loader;

    VAR
      asid_index: integer,
      display_deadstart_screens: boolean,
      sdt_p: mmt$max_sdt_p,
      status: ost$status;

    { Configure the deadstart device and read the first labels from the device.  The deadstart
    { displays will be accessed if there is any error.

    display_deadstart_screens := FALSE;

    /access_deadstart_device/
    WHILE TRUE DO
      IF display_deadstart_screens THEN
        cmp$write_os_status (' ', status);
        cmp$de_configure_ds_device (status);
        cmp$vcmb_menu_manager;
      IFEND;
      display_deadstart_screens := TRUE;

      IF cmv$system_device_data [cmc$sdt_tape_device].specified THEN
        cmp$configure_deadstart_device (cmc$sdt_tape_device, status);
      ELSE
        cmp$configure_deadstart_device (cmc$sdt_disk_device, status);
      IFEND;
      IF NOT status.normal THEN
        CYCLE /access_deadstart_device/;
      IFEND;

      dsp$prepare_deadstart_io (status);
      IF NOT status.normal THEN
        CYCLE /access_deadstart_device/;
      IFEND;

      EXIT /access_deadstart_device/;
    WHILEND /access_deadstart_device/;

    { Set up some variables used by the loading procedures.

    ALLOCATE top_asid_entry_p IN osv$mainframe_wired_heap^;
    top_asid_entry_p^.link_p := NIL;
    current_asid_entry_p := top_asid_entry_p;

    ALLOCATE top_monitor_segment_entry_p IN osv$mainframe_wired_heap^;
    top_monitor_segment_entry_p^.link_p := NIL;
    current_monitor_segment_entry_p := top_monitor_segment_entry_p;

    ALLOCATE top_core_segment_entry_p IN osv$mainframe_wired_heap^;
    top_core_segment_entry_p^.link_p := NIL;
    current_core_segment_entry_p := top_core_segment_entry_p;

    { Reserve the BOOT asids.

    mmp$get_max_sdt_pointer (^jmv$jmtr_xcb, sdt_p);
    FOR asid_index := 0 TO jmv$jmtr_xcb.xp.segment_table_length DO
      IF sdt_p^.st [asid_index].ste.vl <> osc$vl_invalid_entry THEN
        save_asid_entry (sdt_p^.st [asid_index].ste.asid);
      IFEND;
    FOREND;
    save_asid_entry (0);

    { Load the monitor image.

    syp$display_deadstart_message ('Loading the monitor image ...');
    load_monitor_image;

    { Load the system core image.

    syp$display_deadstart_message ('Loading the system core image ...');
    load_system_core_image;

    { Load the DCFILE.

    syp$display_deadstart_message ('Loading the DCFILE ...');
    load_dcfile;

    { Deconfigure the deadstart device.

    cmp$de_configure_ds_device (status);
    IF NOT status.normal THEN
      osp$system_error ('Deconfigure deadstart device failed', ^status);
    IFEND;

    { Initiate the deadstart of system core.

    initiate_deadstart;

  PROCEND dsp$boot_deadstart_loader;
MODEND dsm$boot
