/*                                 TMODULE.C                               */

/*
Object modules are parsed via recursive descent as defined below:

     obj_t_module::      obj_THEADR obj_seg_grp {obj_component} obj_modtail

     obj_seg_grp::       {obj_LNAMES | obj_SEGDEF | obj_EXTDEF}
                         {obj_TYPDEF | obj_EXTDEF | obj_GRPDEF}

     obj_component::     obj_data | obj_debug_record

     obj_data::          obj_content_def | obj_thread_def | obj_COMDEF |
                         obj_TYPDEF | obj_PUBDEF | obj_EXTDEF |
                         obj_FORREF | obj_MODPUB | obj_MODEXT


     obj_debug_record::  obj_LINNUM

     obj_content_def::   obj_data_record {obj_FIXUPP}

     obj_thread_def::    obj_FIXUPP  (containing only thread fields)

     obj_data_record::   obj_LIDATA | obj_LEDATA

     obj_modtail::       obj_MODEND
*/

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                               obj_COMDEF                                |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
bit_16 obj_COMDEF()
BeginDeclarations
bit_32                                 element_count;
bit_32                                 element_size;
bit_8                                  element_type;
bit_8                                  expected_type;
bit_16                                 len;
public_entry_ptr                       pub;
#define Pub                            (*pub)
EndDeclarations
BeginCode
 If Current_record_header.rec_typ IsNot COMDEF_record
  Then
   return(False);
  EndIf;
 While obj_ptr.b8 IsNot end_of_record.b8
  BeginWhile
   If n_externals NotLessThan max_externals.val
    Then
     linker_error(12, "Internal limit exceeded:\n"
                      "\tModule:  \"%Fs\"\n"
                      "\t  File:  \"%Fs\"\n"
                      "\tOffset:  %lu\n"
                      "\t Error:  Too many externals.  Max of %u exceeded.\n"
                      "\t         Retry with larger \"/maxexternals:n\" "
                                  "switch.\n",
                      (*tmodule_name).symbol,
                      (*infile.file_info).filename,
                      current_record_offset,
                      max_externals.val);
    EndIf;
   len         = obj_name_length();
   If case_ignore.val
    Then
     far_to_lower(BytePtr(obj_ptr.b8), len);
    EndIf;
   pub         = lookup_public(len, obj_ptr.b8, 0);
   obj_ptr.b8 += len;
   obj_name_length();  /* Eat the type index. */
   externals[++n_externals] = pub;
   element_type  = *obj_ptr.b8++;
   Using element_type
    BeginCase
     When 0x61:
      expected_type = far_communal;
      element_count = obj_leaf_descriptor();
      element_size  = obj_leaf_descriptor();
      break;
     When 0x62:
      expected_type = near_communal;
      element_size  = obj_leaf_descriptor();
      element_count = 1L;
      break;
     Otherwise:
      linker_error(12, "Translator error:\n"
                       "\tModule:  \"%Fs\"\n"
                       "\t  File:  \"%Fs\"\n"
                       "\tOffset:  %lu\n"
                       "\t Error:  Communal type of \"%02X\" is illegal.\n",
                       (*tmodule_name).symbol,
                       (*infile.file_info).filename,
                       current_record_offset,
                       element_type);
    EndCase;
   If Pub.type_entry Is unused
    Then
     Insert pub AtEnd InList external_list EndInsert;
     Pub.type_entry             = expected_type;
     Pub.Communal.element_size  = element_size;
     Pub.Communal.element_count = element_count;
     Using element_type
     BeginCase
      When 0x61:
       Pub.Communal.next_communal = far_communals;
       far_communals              = pub;
       break;
      When 0x62:
       Pub.Communal.next_communal = near_communals;
       near_communals             = pub;
       break;
     EndCase;
    Else
     If Pub.type_entry Is expected_type
      Then
       If (element_size              * element_count)              Exceeds 
          (Pub.Communal.element_size * Pub.Communal.element_count)
        Then /* We need the largest common */
         Pub.Communal.element_size  = element_size;
         Pub.Communal.element_count = element_count;
        EndIf;
      Else
       If (Pub.type_entry Is near_communal) OrIf
          (Pub.type_entry Is far_communal)
        Then
         linker_error(4, "Translator error:\n"
                         "\tModule:  \"%Fs\"\n"
                         "\t  File:  \"%Fs\"\n"
                         "\tOffset:  %lu\n"
                         "\t Error:  Communal \"%Fs\" is declared both near "
                                     "and far.\n",
                         (*tmodule_name).symbol,
                         (*infile.file_info).filename,
                         current_record_offset,
                         Pub.symbol);
        EndIf;
      EndIf;
    EndIf;
  EndWhile;
 obj_next_record();
 return(True);
EndCode
#undef Pub

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                               obj_COMENT                                |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
bit_16 obj_COMENT()
BeginDeclarations
bit_8                                  comment_class;
EndDeclarations
BeginCode
 If Current_record_header.rec_typ IsNot COMENT_record
  Then
   return(False);
  EndIf;
 obj_ptr.b8++;
 comment_class = *obj_ptr.b8++;
 Using comment_class
  BeginCase
   When 158:
    DOSSEG.val = True;
    break;
   When 161:
    codeview_information_present = True;
    break;
   Otherwise:
    break;
  EndCase;
 return(True);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                             obj_component                               |
  |                                                                         |
  +-------------------------------------------------------------------------+*/

/* obj_component:: obj_data | obj_debug_record */
bit_16 obj_component()
BeginDeclarations
EndDeclarations
BeginCode
 If obj_data() OrIf obj_debug_record()
  Then
   return(True);
  EndIf;
 return(False);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                            obj_content_def                              |
  |                                                                         |
  +-------------------------------------------------------------------------+*/

/* obj_content_def:: obj_data_record {obj_FIXUPP} */
bit_16 obj_content_def()
BeginDeclarations
EndDeclarations
BeginCode
 If Not obj_data_record()
  Then
   return(False);
  EndIf;
 While obj_FIXUPP()
  BeginWhile
  EndWhile;
 return(True);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                               obj_data                                  |
  |                                                                         |
  +-------------------------------------------------------------------------+*/

/* obj_data:: obj_content_def |
              obj_thread_def  |
              obj_TYPDEF      |
              obj_PUBDEF      |
              obj_EXTDEF */

bit_16 obj_data()
BeginDeclarations
EndDeclarations
BeginCode
 If obj_content_def() OrIf
    obj_thread_def()  OrIf
    obj_TYPDEF()      OrIf
    obj_PUBDEF()      OrIf
    obj_EXTDEF()      OrIf
    obj_FORREF()      OrIf
    obj_COMDEF()      OrIf
    obj_MODEXT()      OrIf
    obj_MODPUB()
  Then
   return(True);
  EndIf;
 return(False);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                            obj_data_record                              |
  |                                                                         |
  +-------------------------------------------------------------------------+*/

/*  obj_data_record:: obj_LIDATA | obj_LEDATA */

bit_16 obj_data_record()
BeginDeclarations
EndDeclarations
BeginCode
 If obj_LIDATA() OrIf obj_LEDATA()
  Then
   return(True);
  EndIf;
 return(False);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                           obj_debug_record                              |
  |                                                                         |
  +-------------------------------------------------------------------------+*/

/* obj_debug_record:: obj_LINNUM */

bit_16 obj_debug_record()
BeginDeclarations
EndDeclarations
BeginCode
 If obj_LINNUM()
  Then
   return(True);
  EndIf;
 return(False);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                               obj_EXTDEF                                |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
bit_16 obj_EXTDEF()
BeginDeclarations
bit_16                                 len;
public_entry_ptr                       pub;
#define Pub                            (*pub)
EndDeclarations
BeginCode
 If Current_record_header.rec_typ IsNot EXTDEF_record
  Then
   return(False);
  EndIf;
 While obj_ptr.b8 IsNot end_of_record.b8
  BeginWhile
   If n_externals NotLessThan max_externals.val
    Then
     linker_error(12, "Internal limit exceeded:\n"
                      "\tModule:  \"%Fs\"\n"
                      "\t  File:  \"%Fs\"\n"
                      "\tOffset:  %lu\n"
                      "\t Error:  Too many externals.  Max of %u exceeded.\n"
                      "\t         Retry with larger \"/maxexternals:n\" "
                                  "switch.\n",
                      (*tmodule_name).symbol,
                      (*infile.file_info).filename,
                      current_record_offset,
                      max_externals.val);
    EndIf;
   len         = obj_name_length();
   If case_ignore.val
    Then
     far_to_lower(BytePtr(obj_ptr.b8), len);
    EndIf;
   pub         = lookup_public(len, obj_ptr.b8, 0);
   obj_ptr.b8 += len;
   obj_name_length();  /* Eat the type index. */
   externals[++n_externals] = pub;
   If Pub.type_entry Is unused
    Then
     Insert pub AtEnd InList external_list EndInsert;
     Pub.type_entry = external;
    Else
     If (Pub.type_entry Is public_in_library) AndIf
        (Not Pub.Library.requested)
      Then
       library_request_count++;
       (*Pub.Library.lib_file).request_count++;
       Pub.Library.requested = True;
      EndIf;
    EndIf;
  EndWhile;
 obj_next_record();
 return(True);
EndCode
#undef Pub

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                               obj_FIXUPP                                |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
bit_16 obj_FIXUPP()
BeginDeclarations
EndDeclarations
BeginCode
 If Current_record_header.rec_typ IsNot FIXUPP_record
  Then
   return(False);
  EndIf;
 FIXUPP_contains_only_threads = True;
 While obj_ptr.b8 IsNot end_of_record.b8
  BeginWhile
   If (*obj_ptr.TRD_DAT).type_fixupp_record IsZero
    Then
     obj_FIXUPP_thread();
    Else
     FIXUPP_contains_only_threads = False;
     obj_FIXUPP_fixup();
    EndIf;
  EndWhile;
 obj_next_record();
 return(True);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                           obj_FIXUPP_fixup                              |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
void obj_FIXUPP_fixup()
BeginDeclarations
FIX_DAT_type                           FIX_DAT;
bit_16                                 frame_method;
LOCAT_type                             LOCAT;
bit_16                                 target_method;
bit_8                                  temp;
bit_16                                 thread_number;
EndDeclarations
BeginCode
/*+-------------------------------------------------------------------------+
  |                                                                         |
  | The LOCAT field in a FIXUPP record has its low and high bytes swapped   |
  | because the high order bit must be 0 for threads and 1 for fixups.      |
  | Since that bit could not be placed in the offset, the bytes were        |
  | swapped instead.                                                        |
  |                                                                         |
  +-------------------------------------------------------------------------+*/

 temp                  = obj_ptr.b8[0];
 obj_ptr.b8[0]         = obj_ptr.b8[1];
 obj_ptr.b8[1]         = temp;

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |          Pick up the two required fields (LOCAT and FIX_DAT)            |
  |                                                                         |
  +-------------------------------------------------------------------------+*/

 LOCAT                 = *obj_ptr.LOCAT++;
 FIX_DAT               = *obj_ptr.FIX_DAT++;

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |          A fixup consists of a location, mode, frame and target.        |
  |                         Process the location part.                      |
  |                                                                         |
  +-------------------------------------------------------------------------+*/

 fixup_index           = LOCAT.data_record_offset;
 fixup.location_type   = LOCAT.loc;

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                         Process the mode part.                          |
  |                                                                         |
  +-------------------------------------------------------------------------+*/

 fixup.mode            = LOCAT.m;

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                         Process the frame part.                         |
  |                                                                         |
  +-------------------------------------------------------------------------+*/

 If FIX_DAT.f IsZero
  Then  /* Frame is specified explicitly */
   frame_method         = FIX_DAT.frame;
   fixup.frame_method   = frame_method;
   Using frame_method
    BeginCase
     When 0:
      fixup.frame_referent = (void far *) snames[obj_index_segment()];
      break;
     When 1:
      fixup.frame_referent = (void far *) gnames[obj_index_group()];
      break;
     When 2:
      fixup.frame_referent = (void far *) externals[obj_index_external()];
      break;
     When 3:
      fixup.frame_referent =
       (void far *) (Bit_32(*obj_ptr.b16++) ShiftedLeft 4);
     Otherwise:
      fixup.frame_referent = Null;
      break;
    EndCase;
  Else  /* Frame is specified by a thread */
   thread_number        = FIX_DAT.frame;
   If Not frame_thread[thread_number].thread_defined
    Then
     linker_error(12, "Translator error:\n"
                      "\tModule:  \"%Fs\"\n"
                      "\t  File:  \"%Fs\"\n"
                      "\tOffset:  %lu\n"
                      "\t Error:  Reference to frame thread %u which has "
                                  "been defined.n",
                      (*tmodule_name).symbol,
                      (*infile.file_info).filename,
                      current_record_offset,
                      thread_number);
    EndIf;
   fixup.frame_referent = frame_thread[thread_number].referent;
   fixup.frame_method   = frame_thread[thread_number].method;
  EndIf;

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                         Process the target part.                        |
  |                                                                         |
  +-------------------------------------------------------------------------+*/

 If FIX_DAT.t IsZero
  Then  /* Target is specified explicitly */
   target_method       = FIX_DAT.targt;
   fixup.target_method = target_method;
   Using target_method
    BeginCase
     When 0:  /* Target is the segment referenced by the index */
      fixup.target_referent = (void far *) snames[obj_index_segment()];
      break;
     When 1:  /* Target is the lowest seg in the group referenced 
                 by the index */
      fixup.target_referent = (void far *) gnames[obj_index_group()];
      break;
     When 2:
      fixup.target_referent = (void far *) externals[obj_index_external()];
      break;
     When 3:
      fixup.target_referent =
       (void far *) (Bit_32(*obj_ptr.b16++) ShiftedLeft 4);
      break;
    EndCase;
  Else  /* Target is specified by a thread */
   thread_number         = FIX_DAT.targt;
   If Not target_thread[thread_number].thread_defined
    Then
     linker_error(12, "Translator error:\n"
                      "\tModule:  \"%Fs\"\n"
                      "\t  File:  \"%Fs\"\n"
                      "\tOffset:  %lu\n"
                      "\t Error:  Reference to target thread %u which has "
                                  "been defined.n",
                      (*tmodule_name).symbol,
                      (*infile.file_info).filename,
                      current_record_offset,
                      thread_number);
    EndIf;
   fixup.target_referent = target_thread[thread_number].referent;
   fixup.target_method   = target_thread[thread_number].method;
  EndIf;

 If FIX_DAT.p IsZero
  Then  /* There is a target displacement */
   fixup.target_offset = *obj_ptr.b16++;
  Else  /* The target displacement is zero */
   fixup.target_offset = 0;
  EndIf;

 fixup.external_error_detected = False;

 If (fixup.mode IsZero) AndIf
                             ((fixup.location_type Is base_location)    OrIf
                              (fixup.location_type Is pointer_location) OrIf
                              (fixup.location_type Is hibyte_location))
  Then /* Undefined fixup action */
   linker_error(4, "Possible translator error:\n"
                   "\tModule:  \"%Fs\"\n"
                   "\t  File:  \"%Fs\"\n"
                   "\tOffset:  %lu\n"
                   "\t Error:  Base, pointer or hibyte self-relative fixups\n"
                   "\t         are undefined.\n",
                   (*tmodule_name).symbol,
                   (*infile.file_info).filename,
                   current_record_offset);
  EndIf;

 If last_LxDATA_record_type Is LEDATA_record
  Then
   If ((fixup.location_type Is base_location)     OrIf
       (fixup.location_type Is pointer_location)) AndIf
      (exefile IsTrue)
    Then /* Base and pointer locations will require a relocation item
            in the EXE header */
     n_relocation_items++;
    EndIf;
   write_temp_file(Current_record_header.rec_typ,
                   last_LxDATA_lseg,
                   last_LxDATA_offset + fixup_index,
                   BytePtr(Addr(fixup)),
                   sizeof(fixup));
  Else
   If fixup.mode IsZero
    Then
     linker_error(4, "Translator warning:\n"
                     "\tModule:  \"%Fs\"\n"
                     "\t  File:  \"%Fs\"\n"
                     "\tOffset:  %lu\n"
                     "\t Error:  Self-relative fixup not permitted for "
                                 "LIDATA.\n",
                     (*tmodule_name).symbol,
                     (*infile.file_info).filename,
                     current_record_offset);
    Else
     obj_fixup_LIDATA();
    EndIf;
  EndIf;

 return;
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                            obj_fixup_LIDATA                             |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
void obj_fixup_LIDATA()
BeginDeclarations
obj_ptr_type                           old_obj_ptr;
EndDeclarations
BeginCode
 LIDATA_index  = 0;
 LIDATA_offset = last_LxDATA_offset;
 old_obj_ptr   = obj_ptr;
 obj_ptr.b8    = Last_LIDATA_record_header.variant_part;
 end_of_last_LIDATA_record.b8 =
  (byte *)
   Addr(Last_LIDATA_record_header.variant_part
    [Last_LIDATA_record_header.rec_len-1]);
 obj_index_segment();
 obj_ptr.b16++;
 While obj_ptr.b8 IsNot end_of_last_LIDATA_record.b8
  BeginWhile
   obj_fixup_LIDATA_IDB();
  EndWhile;
 obj_ptr = old_obj_ptr;
 return;
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                           obj_fixup_LIDATA_IDB                          |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
void obj_fixup_LIDATA_IDB()
BeginDeclarations
bit_16                                 block_count;
bit_8                                 *content;
bit_16                                 i;
bit_16                                 j;
bit_16                                 len;
bit_16                                 old_index;
bit_16                                 repeat_count;
EndDeclarations
BeginCode
 repeat_count = *obj_ptr.b16++;  LIDATA_index += sizeof(bit_16);
 block_count  = *obj_ptr.b16++;  LIDATA_index += sizeof(bit_16);
 content      = obj_ptr.b8;
 old_index    = LIDATA_index;
 If block_count IsNotZero
  Then  /* Handle recursive case:  Content is iterated data block */
   For i=0; i<repeat_count; i++
    BeginFor
     obj_ptr.b8 = content;
     LIDATA_index = old_index;
     For j=0; j<block_count; j++
      BeginFor
       obj_fixup_LIDATA_IDB();
      EndFor;
    EndFor;
  Else  /* Handle non-recursive case:  Content is data. */
   For i=0; i<repeat_count; i++
    BeginFor
     obj_ptr.b8   = content;
     LIDATA_index = old_index;
     len          = Bit_16(*obj_ptr.b8++);  LIDATA_index += sizeof(bit_8);
     If (fixup_index NotLessThan LIDATA_index)        AndIf
        (fixup_index LessThan   (LIDATA_index + len))
      Then
       write_temp_file(Current_record_header.rec_typ,
                       last_LxDATA_lseg,
                       LIDATA_offset + fixup_index - LIDATA_index,
                       BytePtr(Addr(fixup)),
                       sizeof(fixup));
       If ((fixup.location_type Is base_location)     OrIf
           (fixup.location_type Is pointer_location)) AndIf
          (exefile IsTrue)
        Then /* Base and pointer locations will require a relocation item
                in the EXE header */
         n_relocation_items++;
        EndIf;
      EndIf;
     LIDATA_offset += len;
    EndFor;
   obj_ptr.b8    += len;
   LIDATA_index  += len;
  EndIf;
 return;
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                           obj_FIXUPP_thread                             |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
void obj_FIXUPP_thread()
BeginDeclarations
bit_16                                 method;
bit_16                                 thread;
TRD_DAT_type                           TRD_DAT;
EndDeclarations
BeginCode
 TRD_DAT = *obj_ptr.TRD_DAT++;
 thread  = TRD_DAT.thred;
 method  = TRD_DAT.method;
 If TRD_DAT.d IsZero
  Then  /* This is a target thread */
   target_thread[thread].method = Bit_8(method);
   target_thread[thread].thread_defined = True;
   Using method
    BeginCase
     When 0:
      target_thread[thread].referent =
       (void far *) snames[obj_index_segment()];
      break;
     When 1:
      target_thread[thread].referent =
       (void far *) gnames[obj_index_group()];
      break;
     When 2:
      target_thread[thread].referent =
       (void far *) externals[obj_index_external()];
      break;
     When 3:
      target_thread[thread].referent =
       (void far *) (Bit_32(*obj_ptr.b16++) ShiftedLeft 4);
     Otherwise:
      target_thread[thread].referent = Null;
      break;
    EndCase;
  Else  /* This is a frame thread */
   frame_thread[thread].method = Bit_8(method);
   frame_thread[thread].thread_defined = True;
   Using method
    BeginCase
     When 0:
      frame_thread[thread].referent =
       (void far *) snames[obj_index_segment()];
      break;
     When 1:
      frame_thread[thread].referent =
       (void far *) gnames[obj_index_group()];
      break;
     When 2:
      frame_thread[thread].referent =
       (void far *) externals[obj_index_external()];
      break;
     When 3:
      frame_thread[thread].referent =
       (void far *) (Bit_32(*obj_ptr.b16++) ShiftedLeft 4);
     Otherwise:
      frame_thread[thread].referent = Null;
      break;
    EndCase;
  EndIf;
 return;
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                               obj_FORREF                                |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
bit_16 obj_FORREF()
BeginDeclarations
bit_16                                 len;
bit_16                                 segment_index;
EndDeclarations
BeginCode
 If Current_record_header.rec_typ IsNot FORREF_record
  Then
   return(False);
  EndIf;
 segment_index = obj_index_segment();
 len           = Current_record_header.rec_len - 2;
 If segment_index Exceeds 127
  Then
   len--;
  EndIf;
 write_temp_file(Current_record_header.rec_typ,
                 snames[segment_index],
                 0,
                 BytePtr(obj_ptr.b8),
                 len);
 obj_next_record();
 return(True);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                           obj_generate_segment                          |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
lseg_ptr obj_generate_segment(lname_entry_ptr segment_lname,
                              lname_entry_ptr class_lname,
                              combine_type    combine,
                              bit_8           align,
                              lname_entry_ptr tmodule,
                              file_info_ptr   file,
                              bit_32          address,
                              bit_32          length)
BeginDeclarations
lseg_ptr                               lseg;
#define Lseg                           (*lseg)
segment_entry_ptr                      seg;
#define Seg                            (*seg)
EndDeclarations
BeginCode
 If combine Is stack_combine
  Then
   length += AlignmentGap(length, 1L); /* Stacks should be an integral
                                          number of words. */
  EndIf;
 seg             = lookup_segment(segment_lname, class_lname, combine);
 If (combine IsNot common_combine) OrIf (Seg.lsegs.first IsNull)
  Then
   Seg.address   = address;
   Seg.length   += length;
   lseg          = (lseg_ptr) 
                    allocate_memory(Addr(static_pool),
                                    Bit_32(sizeof(lseg_type)));
   Lseg.segment  = seg;
   Lseg.tmodule  = tmodule;
   Lseg.file     = file;
   Lseg.address  = address;
   Lseg.length   = length;
   Lseg.align    = align;
   If (combine IsNot common_combine)      AndIf
      (combine IsNot blank_common_combine)
    Then  /* Don't allocate common data yet.  (We will wait until we
             know how long the common block will be.) */
     Lseg.data   = allocate_memory(Addr(static_pool), length);
    EndIf;

   Lseg.highest_uninitialized_byte = 0L;

   Insert lseg AtEnd InList Seg.lsegs EndInsert;
  Else  /* Not the first occurrence of this common */
   lseg = Seg.lsegs.first;
   If length Exceeds Seg.length
    Then  /* Expand common block to be big enough to hold this entry. */
     Seg.length  =
     Lseg.length = length;
    EndIf;
   If align Exceeds Lseg.align
    Then  /* Align to largest boundary. */
     Lseg.align = align;
    EndIf;
  EndIf;
 If Seg.combine Is stack_combine
  Then
   If Not stack_segment_found
    Then
     largest_stack_seg        = seg;
     largest_stack_seg_length = Seg.length;
     stack_segment_found      = True;
    Else
     If Seg.length Exceeds largest_stack_seg_length
      Then
       largest_stack_seg        = seg;
       largest_stack_seg_length = Seg.length;
      EndIf;
    EndIf;
  EndIf;
 return(lseg);
EndCode
#undef Lseg 
#undef Seg

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                               obj_GRPDEF                                |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
bit_16 obj_GRPDEF()
BeginDeclarations
group_entry_ptr                        group;
#define Group                          (*group)
bit_16                                 group_index;
lseg_ptr                               lseg;
#define Lseg                           (*lseg)
segment_entry_ptr                      seg;
#define Seg                            (*seg)
bit_16                                 segment_index;
EndDeclarations
BeginCode
 If Current_record_header.rec_typ IsNot GRPDEF_record
  Then
   return(False);
  EndIf;
 group_index         = obj_index_LNAME();
 group               = lookup_group(lnames[group_index]);
 If n_groups NotLessThan max_groups.val
  Then
   linker_error(12, "Internal limit exceeded:\n"
                    "\tModule:  \"%Fs\"\n"
                    "\t  File:  \"%Fs\"\n"
                    "\tOffset:  %lu\n"
                    "\t Error:  Too many GRPDEFs.  Max of %u exceeded.\n"
                    "\t         Retry with larger \"/maxgroups:n\" switch.\n",
                    (*tmodule_name).symbol,
                    (*infile.file_info).filename,
                    current_record_offset,
                    max_groups.val);
  EndIf;
 gnames[++n_groups]  = group;
 While obj_ptr.b8 IsNot end_of_record.b8
  BeginWhile
   If *obj_ptr.b8++ IsNot 0xFF
    Then
     linker_error(12, "Translator error:\n"
                      "\tModule:  \"%Fs\"\n"
                      "\t  File:  \"%Fs\"\n"
                      "\tOffset:  %lu\n"
                      "\t Error:  GRPDEF record has a group component "
                                  "descriptor which\n"
                      "\t         does not start with 0xFF.\n",
                      (*tmodule_name).symbol,
                      (*infile.file_info).filename,
                      current_record_offset);
    EndIf;
   segment_index = obj_index_segment();
   lseg          = snames[segment_index];
   seg           = Lseg.segment;
   If Seg.owning_group IsNull
    Then
     Seg.owning_group = group;
    Else
     If Seg.owning_group IsNot group
      Then
       linker_error(4, "Attempt to place segment \"%Fs\" into group \"%Fs\"\n"
                       "\twhen it is already in group \"%Fs\".\n"
                       "\tRequest to place in group \"%Fs\" ignored.\n",
                       (*Seg.segment_name).symbol, (*Group.group_name).symbol,
                       (*(*Seg.owning_group).group_name).symbol,
                       (*Group.group_name).symbol);
     EndIf;
    EndIf;
  EndWhile;
 obj_next_record();
 return(True);
EndCode
#undef Seg
#undef Group
#undef Lseg

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                           obj_index_external                            |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
bit_16 obj_index_external()
BeginDeclarations
bit_16                                 index;
EndDeclarations
BeginCode
 If *obj_ptr.b8 LessThan 128
  Then
   index = Bit_16(*obj_ptr.b8++);
  Else
   index = (Bit_16(*obj_ptr.b8++ - 128) ShiftedLeft 8) +
           Bit_16(*obj_ptr.b8++);
  EndIf;
   If index Exceeds n_externals
    Then
     linker_error(12, "Translator error:\n"
                      "\tModule:  \"%Fs\"\n"
                      "\t  File:  \"%Fs\"\n"
                      "\tOffset:  %lu\n"
                      "\t Error:  Invalid external index (%u) with only %u "
                                  "externals defined.\n",
                      (*tmodule_name).symbol,
                      (*infile.file_info).filename,
                      current_record_offset,
                      index, n_externals);
    EndIf;
 return(index);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                             obj_index_group                             |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
bit_16 obj_index_group()
BeginDeclarations
bit_16                                 index;
EndDeclarations
BeginCode
 If *obj_ptr.b8 LessThan 128
  Then
   index = Bit_16(*obj_ptr.b8++);
  Else
   index = (Bit_16(*obj_ptr.b8++ - 128) ShiftedLeft 8) +
           Bit_16(*obj_ptr.b8++);
  EndIf;
   If index Exceeds n_groups
    Then
     linker_error(12, "Translator error:\n"
                      "\tModule:  \"%Fs\"\n"
                      "\t  File:  \"%Fs\"\n"
                      "\tOffset:  %lu\n"
                      "\t Error:  Invalid group index (%u) with only %u "
                                  "groups defined.\n",
                      (*tmodule_name).symbol,
                      (*infile.file_info).filename,
                      current_record_offset,
                      index, n_groups);
    EndIf;
 return(index);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                             obj_index_LNAME                             |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
bit_16 obj_index_LNAME()
BeginDeclarations
bit_16                                 index;
EndDeclarations
BeginCode
 If *obj_ptr.b8 LessThan 128
  Then
   index = Bit_16(*obj_ptr.b8++);
  Else
   index = (Bit_16(*obj_ptr.b8++ - 128) ShiftedLeft 8) +
           Bit_16(*obj_ptr.b8++);
  EndIf;
   If index Exceeds n_lnames
    Then
     linker_error(12, "Translator error:\n"
                      "\tModule:  \"%Fs\"\n"
                      "\t  File:  \"%Fs\"\n"
                      "\tOffset:  %lu\n"
                      "\t Error:  Invalid LNAME index (%u) with only %u "
                                  "LNAMEs defined.\n",
                      (*tmodule_name).symbol,
                      (*infile.file_info).filename,
                      current_record_offset,
                      index, n_lnames);
    EndIf;
 return(index);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                           obj_index_segment                             |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
bit_16 obj_index_segment()
BeginDeclarations
bit_16                                 index;
EndDeclarations
BeginCode
 If *obj_ptr.b8 LessThan 128
  Then
   index = Bit_16(*obj_ptr.b8++);
  Else
   index = (Bit_16(*obj_ptr.b8++ - 128) ShiftedLeft 8) +
           Bit_16(*obj_ptr.b8++);
  EndIf;
   If index Exceeds n_segments
    Then
     linker_error(12, "Translator error:\n"
                      "\tModule:  \"%Fs\"\n"
                      "\t  File:  \"%Fs\"\n"
                      "\tOffset:  %lu\n"
                      "\t Error:  Invalid segment index (%u) with only %u "
                                  "segments defined.\n",
                      (*tmodule_name).symbol,
                      (*infile.file_info).filename,
                      current_record_offset,
                      index, n_segments);
    EndIf;
 return(index);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                         obj_iterated_data_block                         |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
void obj_iterated_data_block()
BeginDeclarations
bit_16                                 block_count;
bit_8                                 *content;
bit_16                                 i;
bit_16                                 j;
bit_16                                 len;
bit_16                                 repeat_count;
EndDeclarations
BeginCode
 repeat_count = *obj_ptr.b16++;
 block_count  = *obj_ptr.b16++;
 If block_count IsNotZero
  Then  /* Handle recursive case:  Content is iterated data block */
   content = obj_ptr.b8;
   For i=0; i<repeat_count; i++
    BeginFor
     obj_ptr.b8 = content;
     For j=0; j<block_count; j++
      BeginFor
       obj_iterated_data_block();
      EndFor;
    EndFor;
  Else  /* Handle non-recursive case:  Content is data. */
   len = Bit_16(*obj_ptr.b8++);
   For i=0; i<repeat_count; i++
    BeginFor
     far_move(Addr(last_LxDATA_Lseg.data[LIDATA_offset]), 
              obj_ptr.b8, len);
     LIDATA_offset += len;
    EndFor;
   obj_ptr.b8 += len;
  EndIf;
 return;
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                     obj_iterated_data_block_length                      |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
bit_32 obj_iterated_data_block_length()
BeginDeclarations
bit_16                                 block_count;
bit_16                                 i;
bit_16                                 len;
bit_32                                 length;
bit_16                                 repeat_count;
EndDeclarations
BeginCode
 repeat_count = *obj_ptr.b16++;
 block_count  = *obj_ptr.b16++;
 If repeat_count IsZero
  Then /* This is a translator error. */
   linker_error(12, "Translator error:\n"
                    "\tModule:  \"%Fs\"\n"
                    "\t  File:  \"%Fs\"\n"
                    "\tOffset:  %lu\n"
                    "\t Error:  Repeat count in LIDATA iterated data block "
                                "is zero.\n",
                    (*tmodule_name).symbol,
                    (*infile.file_info).filename,
                    current_record_offset);
  EndIf;
 length       = 0L;
 If block_count IsNotZero
  Then  /* Handle recursive case:  Content is iterated data block */
   For i=0; i<block_count; i++
    BeginFor
     length     += Bit_32(repeat_count) * obj_iterated_data_block_length();
    EndFor;
  Else  /* Handle non-recursive case:  Content is data. */
   len         = Bit_16(*obj_ptr.b8++);
   obj_ptr.b8 += len;
   length      = Bit_32(repeat_count) * Bit_32(len);
  EndIf;
 return(length);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                           obj_leaf_descriptor                           |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
bit_32 obj_leaf_descriptor()
BeginDeclarations
bit_8                                  element_size;
EndDeclarations
BeginCode
 element_size = *obj_ptr.b8++;
 If element_size LessThan 129
  Then
   return(Bit_32(element_size));
  Else
   If element_size Is 129
    Then
     return(Bit_32(*obj_ptr.b16++));
    Else
     If element_size Is 132
      Then
       obj_ptr.b8--;
       return((*obj_ptr.b32++) And 0x00FFFFFFL);
      Else
       If element_size Is 136
        Then
         return(*obj_ptr.b32++);
        Else
         linker_error(12, "Translator error:\n"
                        "\tModule:  \"%Fs\"\n"
                        "\t  File:  \"%Fs\"\n"
                        "\tOffset:  %lu\n"
                        "\t Error:  Communal element size of %u is illegal.\n",
                        (*tmodule_name).symbol,
                        (*infile.file_info).filename,
                        current_record_offset,
                        element_size);
        EndIf;
      EndIf;
    EndIf;
  EndIf;
 return(0L);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                               obj_LEDATA                                |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
bit_16 obj_LEDATA()
BeginDeclarations
bit_32                                 next_byte;
bit_16                                 len;
lseg_ptr                               lseg;
#define Lseg                           (*lseg)
bit_16                                 offset;
bit_16                                 segment_index;
EndDeclarations
BeginCode
 If Current_record_header.rec_typ IsNot LEDATA_record
  Then
   return(False);
  EndIf;
 last_LxDATA_record_type = Current_record_header.rec_typ;
 segment_index           = obj_index_segment();
 last_LxDATA_lseg        =
 lseg                    = snames[segment_index];
 len                     = Current_record_header.rec_len - 4;
 If segment_index Exceeds 127
  Then
   len--;
  EndIf;
 last_LxDATA_offset =
 offset             = *obj_ptr.b16++;
 next_byte          = Bit_32(offset) + Bit_32(len);
 If next_byte Exceeds Lseg.length
  Then
   linker_error(12, "Translator error:\n"
                    "\tModule:  \"%Fs\"\n"
                    "\t  File:  \"%Fs\"\n"
                    "\tOffset:  %lu\n"
                    "\t Error:  Attempt to initialize past end of LSEG.\n",
                    (*tmodule_name).symbol,
                    (*infile.file_info).filename,
                    current_record_offset);
  EndIf;
 If next_byte Exceeds Lseg.highest_uninitialized_byte
  Then
   Lseg.highest_uninitialized_byte = next_byte;
  EndIf;
 If (*last_LxDATA_Lseg.segment).combine IsNot common_combine
  Then
   far_move(Addr(Lseg.data[offset]), obj_ptr.b8, len);
  Else  /* We must save the initialization data out to the tmp file until
           later when we know the length. */
   write_temp_file(Current_record_header.rec_typ,
                   last_LxDATA_lseg,
                   last_LxDATA_offset,
                   BytePtr(obj_ptr.b8),
                   len);
  EndIf;
 obj_next_record();
 return(True);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                               obj_LIDATA                                |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
bit_16 obj_LIDATA()
BeginDeclarations
bit_16                                 len;
bit_32                                 LIDATA_length;
bit_32                                 next_byte;
bit_16                                 segment_index;
EndDeclarations
BeginCode
 If Current_record_header.rec_typ IsNot LIDATA_record
  Then
   return(False);
  EndIf;
 far_move(BytePtr(last_LIDATA_record), 
          BytePtr(object_file_element),
          Current_record_header.rec_len + sizeof(obj_record_header_type) - 1);
 last_LxDATA_record_type = Current_record_header.rec_typ;
 segment_index           = obj_index_segment();
 last_LxDATA_lseg        = snames[segment_index];
 LIDATA_offset           =
 last_LxDATA_offset      = *obj_ptr.b16++;
 LIDATA_length           = obj_LIDATA_length();
 next_byte               = last_LxDATA_offset + LIDATA_length;
 If next_byte Exceeds last_LxDATA_Lseg.length
  Then
   linker_error(12, "Translator error:\n"
                    "\tModule:  \"%Fs\"\n"
                    "\t  File:  \"%Fs\"\n"
                    "\tOffset:  %lu\n"
                    "\t Error:  Attempt to initialize past end of LSEG.\n",
                    (*tmodule_name).symbol,
                    (*infile.file_info).filename,
                    current_record_offset);
  EndIf;
 If next_byte Exceeds last_LxDATA_Lseg.highest_uninitialized_byte
  Then
   last_LxDATA_Lseg.highest_uninitialized_byte = next_byte;
  EndIf;
 If (*last_LxDATA_Lseg.segment).combine IsNot common_combine
  Then
   While obj_ptr.b8 IsNot end_of_record.b8
   BeginWhile
   obj_iterated_data_block();
   EndWhile;
  Else  /* We must save the initialization data out to the tmp file until
           later when we know the length. */
   len                     = Current_record_header.rec_len - 4;
   If segment_index Exceeds 127
    Then
     len--;
    EndIf;
   write_temp_file(Current_record_header.rec_typ,
                   last_LxDATA_lseg,
                   last_LxDATA_offset,
                   BytePtr(obj_ptr.b8),
                   len);
  EndIf;
 obj_next_record();
 return(True);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                           obj_LIDATA_length                             |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
bit_32 obj_LIDATA_length()
BeginDeclarations
bit_32                                 length;
bit_8                                  *start;
EndDeclarations
BeginCode
 start  = obj_ptr.b8;
 length = 0L;
 While obj_ptr.b8 IsNot end_of_record.b8
  BeginWhile
   length += obj_iterated_data_block_length();
  EndWhile;
 obj_ptr.b8 = start;
 return(length);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                               obj_LINNUM                                |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
bit_16 obj_LINNUM()
BeginDeclarations
EndDeclarations
BeginCode
 If Current_record_header.rec_typ IsNot LINNUM_record
  Then
   return(False);
  EndIf;
 obj_next_record();
 return(True);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                               obj_LNAMES                                |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
bit_16 obj_LNAMES()
BeginDeclarations
EndDeclarations
BeginCode
 If Current_record_header.rec_typ IsNot LNAMES_record
  Then
   return(False);
  EndIf;
 While obj_ptr.b8 IsNot end_of_record.b8
  BeginWhile
   If n_lnames NotLessThan max_lnames.val
    Then
     linker_error(12, "Internal limit exceeded:\n"
                      "\tModule:  \"%Fs\"\n"
                      "\t  File:  \"%Fs\"\n"
                      "\tOffset:  %lu\n"
                      "\t Error:  Too many LNAMES.  Max of %u exceeded.\n"
                      "\t         Retry with larger \"/maxlnames:n\" switch.\n",
                      (*tmodule_name).symbol,
                      (*infile.file_info).filename,
                      current_record_offset,
                      max_lnames.val);
    EndIf;
   lnames[++n_lnames] = obj_name();
  EndWhile;
 obj_next_record();
 return(True);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                               obj_MODEND                                |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
bit_16 obj_MODEND()
BeginDeclarations
FIX_DAT_type                           END_DAT;
bit_16                                 frame_method;
MOD_TYP_type                           MOD_TYP;
bit_16                                 target_method;
bit_16                                 thread_number;
EndDeclarations
BeginCode
 If Current_record_header.rec_typ IsNot MODEND_record
  Then
   return(False);
  EndIf;
 MOD_TYP = *obj_ptr.MOD_TYP++;

 If MOD_TYP.zeros IsNotZero
  Then
   linker_error(4, "Translator error:\n"
                   "\tModule:  \"%Fs\"\n"
                   "\t  File:  \"%Fs\"\n"
                   "\tOffset:  %lu\n"
                   "\t Error:  Bits 1 thru 5 of MOD TYP must be zero.\n",
                   (*tmodule_name).symbol,
                   (*infile.file_info).filename,
                   current_record_offset);
  EndIf;

 If (MOD_TYP.mattr IsNot 1) AndIf (MOD_TYP.mattr IsNot 3)
  Then  /* We have no starting address */
   return(True);
  EndIf;

 If MOD_TYP.l IsNot 1
  Then
   linker_error(4, "Translator error:\n"
                   "\tModule:  \"%Fs\"\n"
                   "\t  File:  \"%Fs\"\n"
                   "\tOffset:  %lu\n"
                   "\t Error:  Bit 0 of MOD TYP must be one.\n",
                   (*tmodule_name).symbol,
                   (*infile.file_info).filename,
                   current_record_offset);
  EndIf;

 If start_address_found IsTrue
  Then
   linker_error(4, "Multiple start address encountered.  The start address\n"
                   "in module \"%Fs\" of file \"%Fs\" has been ignored.\n",
                   (*tmodule_name).symbol,
                   (*infile.file_info).filename);
  EndIf;

 start_address_found = True;

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                  Pick up the required field END_DAT.                    |
  |                                                                         |
  +-------------------------------------------------------------------------+*/

 END_DAT = *obj_ptr.FIX_DAT++;

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                         Process the frame part.                         |
  |                                                                         |
  +-------------------------------------------------------------------------+*/

 If END_DAT.f IsZero
  Then  /* Frame is specified explicitly */
   frame_method                 = END_DAT.frame;
   start_address.frame_method   = frame_method;
   Using frame_method
    BeginCase
     When 0:
      start_address.frame_referent =
       (void far *) snames[obj_index_segment()];
      break;
     When 1:
      start_address.frame_referent =
       (void far *) gnames[obj_index_group()];
      break;
     When 2:
      start_address.frame_referent =
       (void far *) externals[obj_index_external()];
      break;
     When 3:
      start_address.frame_referent =
       (void far *) (Bit_32(*obj_ptr.b16++) ShiftedLeft 4);
     Otherwise:
      start_address.frame_referent = Null;
      break;
    EndCase;
  Else  /* Frame is specified by a thread */
   thread_number                = END_DAT.frame;
   If Not frame_thread[thread_number].thread_defined
    Then
     linker_error(12, "Translator error:\n"
                      "\tModule:  \"%Fs\"\n"
                      "\t  File:  \"%Fs\"\n"
                      "\tOffset:  %lu\n"
                      "\t Error:  Reference to frame thread %u which has "
                                  "been defined.n",
                      (*tmodule_name).symbol,
                      (*infile.file_info).filename,
                      current_record_offset,
                      thread_number);
    EndIf;
   start_address.frame_referent = frame_thread[thread_number].referent;
   start_address.frame_method   = frame_thread[thread_number].method;
  EndIf;

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                         Process the target part.                        |
  |                                                                         |
  +-------------------------------------------------------------------------+*/

 If END_DAT.t IsZero
  Then  /* Target is specified explicitly */
   target_method               = END_DAT.targt;
   start_address.target_method = target_method;
   Using target_method
    BeginCase
     When 0:
      start_address.target_referent =
       (void far *) snames[obj_index_segment()];
      break;
     When 1:
      start_address.target_referent =
       (void far *) gnames[obj_index_group()];
      break;
     When 2:
      start_address.target_referent =
       (void far *) externals[obj_index_external()];
      break;
     When 3:
      start_address.target_referent =
       (void far *) (Bit_32(*obj_ptr.b16++) ShiftedLeft 4);
      break;
    EndCase;
  Else  /* Target is specified by a thread */
   thread_number                 = END_DAT.targt;
   If Not target_thread[thread_number].thread_defined
    Then
     linker_error(12, "Translator error:\n"
                      "\tModule:  \"%Fs\"\n"
                      "\t  File:  \"%Fs\"\n"
                      "\tOffset:  %lu\n"
                      "\t Error:  Reference to target thread %u which has "
                                  "been defined.n",
                      (*tmodule_name).symbol,
                      (*infile.file_info).filename,
                      current_record_offset,
                      thread_number);
    EndIf;
   start_address.target_referent = target_thread[thread_number].referent;
   start_address.target_method   = target_thread[thread_number].method;
  EndIf;

 If END_DAT.p IsZero
  Then  /* There is a target displacement */
   start_address.target_offset = *obj_ptr.b16++;
  Else  /* The target displacement is zero */
   linker_error(12, "Translator error:\n"
                    "\tModule:  \"%Fs\"\n"
                    "\t  File:  \"%Fs\"\n"
                    "\tOffset:  %lu\n"
                    "\t Error:  Only primary fixups allowed in MODEND.\n",
                    (*tmodule_name).symbol,
                    (*infile.file_info).filename,
                    current_record_offset);
   start_address.target_offset = 0;
  EndIf;
 return(True);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                               obj_MODEXT                                |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
bit_16 obj_MODEXT()
BeginDeclarations
bit_16                                 len;
public_entry_ptr                       pub;
#define Pub                            (*pub)
EndDeclarations
BeginCode
 If Current_record_header.rec_typ IsNot MODEXT_record
  Then
   return(False);
  EndIf;
 While obj_ptr.b8 IsNot end_of_record.b8
  BeginWhile
   If n_externals NotLessThan max_externals.val
    Then
     linker_error(12, "Internal limit exceeded:\n"
                      "\tModule:  \"%Fs\"\n"
                      "\t  File:  \"%Fs\"\n"
                      "\tOffset:  %lu\n"
                      "\t Error:  Too many externals.  Max of %u exceeded.\n"
                      "\t         Retry with larger \"/maxexternals:n\" "
                                  "switch.\n",
                      (*tmodule_name).symbol,
                      (*infile.file_info).filename,
                      current_record_offset,
                      max_externals.val);
    EndIf;
   len         = obj_name_length();
   If case_ignore.val
    Then
     far_to_lower(BytePtr(obj_ptr.b8), len);
    EndIf;
   pub         = lookup_public(len, obj_ptr.b8, tmodule_number);
   obj_ptr.b8 += len;
   obj_name_length();  /* Eat the type index. */
   externals[++n_externals] = pub;
   If Pub.type_entry Is unused
    Then
     Insert pub AtEnd InList external_list EndInsert;
     Pub.type_entry = external;
    EndIf;
  EndWhile;
 obj_next_record();
 return(True);
EndCode
#undef Pub

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                               obj_MODPUB                                |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
bit_16 obj_MODPUB()
BeginDeclarations
bit_16                                 group_index;
bit_16                                 frame;
bit_16                                 len;
public_entry_ptr                       pub;
#define Pub                            (*pub)
bit_16                                 segment_index;
EndDeclarations
BeginCode
 If Current_record_header.rec_typ IsNot MODPUB_record
  Then
   return(False);
  EndIf;
 group_index = obj_index_group();
 segment_index = obj_index_segment();
 If (segment_index IsZero) AndIf (group_index IsZero)
  Then
   frame = *obj_ptr.b16++;
  EndIf;
 While obj_ptr.b8 IsNot end_of_record.b8
  BeginWhile
   len = obj_name_length();
   If case_ignore.val
    Then
     far_to_lower(BytePtr(obj_ptr.b8), len);
    EndIf;
   pub = lookup_public(len, obj_ptr.b8, tmodule_number);
   obj_ptr.b8 += len;
   If Pub.type_entry Is internal
    Then
     linker_error(4, "Duplicate definition of public \"%Fs\".\n"
                     "\tDefinition in module \"%Fs\" of file \"%Fs\" "
                     "ignored.\n",
                     Pub.symbol,
                     (*tmodule_name).symbol,(*infile.file_info).filename);
     obj_ptr.b16++;      /* Eat offset. */
     obj_name_length();  /* Eat type index. */
    Else
     If Pub.type_entry Is unused
      Then
       Insert pub AtEnd InList external_list EndInsert;
      EndIf;
     Pub.type_entry       = internal;
     Pub.Internal.group   = gnames[group_index];
     Pub.Internal.lseg    = snames[segment_index];
     Pub.Internal.frame   = frame;
     Pub.Internal.offset  = *obj_ptr.b16++;
     obj_name_length();  /* Eat type index. */
    EndIf;
  EndWhile;
 obj_next_record();
 return(True);
EndCode
#undef Pub

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                              obj_modtail                                |
  |                                                                         |
  +-------------------------------------------------------------------------+*/

/* obj_modtail:: obj_MODEND */

bit_16 obj_modtail()
BeginDeclarations
EndDeclarations
BeginCode
 If obj_MODEND()
  Then
   return(True);
  EndIf;
 return(False);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                               obj_name                                  |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
lname_entry_ptr obj_name()
BeginDeclarations
lname_entry_ptr                        name;
bit_16                                 len;
EndDeclarations
BeginCode
 len = obj_name_length();
 If len IsZero
  Then
   name = none_lname;
  Else
   If case_ignore.val
    Then
     far_to_lower(BytePtr(obj_ptr.b8), len);
    EndIf;
   name        = lookup_lname(len, obj_ptr.b8);
   obj_ptr.b8 += len;
  EndIf;
 return(name);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                            obj_name_length                              |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
bit_16 obj_name_length()
BeginDeclarations
EndDeclarations
BeginCode
 If *obj_ptr.b8 LessThan 128
  Then
   return(Bit_16(*obj_ptr.b8++));
  Else
   return((Bit_16(*obj_ptr.b8++ - 128) ShiftedLeft 8) +
          (Bit_16(*obj_ptr.b8++)));
  EndIf;
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                             obj_next_record                             |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
void obj_next_record()
BeginDeclarations
EndDeclarations
BeginCode
 Repeat
  BeginRepeat
   file_read(object_file_element,  sizeof(obj_record_header_type) - 1);
   While (Current_record_header.rec_typ Is LINNUM_record) OrIf
         ((Current_record_header.rec_typ Is COMENT_record) AndIf
          (Current_record_header.rec_len Exceeds MAX_OBJECT_FILE_READ_SIZE))
    BeginWhile
     file_position(Bit_32(infile.byte_position) +
                   infile.start_of_buffer_position +
                   Bit_32(Current_record_header.rec_len));
     file_read(object_file_element,  sizeof(obj_record_header_type) - 1);
    EndWhile;
   current_record_offset = Bit_32(infile.byte_position) +
                           infile.start_of_buffer_position -
                           Bit_32(sizeof(obj_record_header_type)-1);
   If Current_record_header.rec_len Exceeds MAX_OBJECT_FILE_READ_SIZE
    Then
     linker_error(12, "Probable invalid OBJ format "
                      "or possible translator error:\n"
                      "\tModule:  \"%Fs\"\n"
                      "\t  File:  \"%Fs\"\n"
                      "\tOffset:  %lu\n"
                      "\t Error:  Record too long.\n"
                      "\t         Max record length supported by this "
                                 "linker is %u bytes.\n",
                      (*tmodule_name).symbol,
                      (*infile.file_info).filename,
                      current_record_offset,
                      MAX_OBJECT_FILE_READ_SIZE);
    EndIf;
   file_read(Current_record_header.variant_part, 
             Current_record_header.rec_len);
   If (objchecksum.val IsTrue) AndIf
      (Bit_8(checksum(Current_record_header.rec_len +
                      sizeof(obj_record_header_type)-1,
                     (byte *) current_record_header)) IsNotZero)
    Then
     linker_error(12, "Translator error:\n"
                      "\tModule:  \"%Fs\"\n"
                      "\t  File:  \"%Fs\"\n"
                      "\tOffset:  %lu\n"
                      "\t Error:  Checksum error.\n",
                      (*tmodule_name).symbol,
                      (*infile.file_info).filename,
                      current_record_offset);
    EndIf;
   obj_ptr.b8 = Current_record_header.variant_part;
   end_of_record.b8 =
    (byte *)
     Addr(Current_record_header.variant_part[Current_record_header.rec_len-1]);
   RepeatIf obj_COMENT()
  EndRepeat;
 return;
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                               obj_PUBDEF                                |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
bit_16 obj_PUBDEF()
BeginDeclarations
bit_16                                 group_index;
bit_16                                 frame;
bit_16                                 len;
public_entry_ptr                       pub;
#define Pub                            (*pub)
bit_16                                 segment_index;
EndDeclarations
BeginCode
 If Current_record_header.rec_typ IsNot PUBDEF_record
  Then
   return(False);
  EndIf;
 group_index = obj_index_group();
 segment_index = obj_index_segment();
 If (segment_index IsZero) AndIf (group_index IsZero)
  Then
   frame = *obj_ptr.b16++;
  EndIf;
 While obj_ptr.b8 IsNot end_of_record.b8
  BeginWhile
   len = obj_name_length();
   If case_ignore.val
    Then
     far_to_lower(BytePtr(obj_ptr.b8), len);
    EndIf;
   pub = lookup_public(len, obj_ptr.b8, 0);
   obj_ptr.b8 += len;
   If Pub.type_entry Is internal
    Then
     linker_error(4, "Duplicate definition of public \"%Fs\".\n"
                     "\tDefinition in module \"%Fs\" of file \"%Fs\" "
                     "ignored.\n",
                     Pub.symbol,
                     (*tmodule_name).symbol,(*infile.file_info).filename);
     obj_ptr.b16++;      /* Eat offset. */
     obj_name_length();  /* Eat type index. */
    Else
     If Pub.type_entry Is unused
      Then
       Insert pub AtEnd InList external_list EndInsert;
      EndIf;
     If (Pub.type_entry Is public_in_library) AndIf
        (Pub.Library.requested)
      Then
       library_request_count--;
       (*Pub.Library.lib_file).request_count--;
      EndIf;
     Pub.type_entry       = internal;
     Pub.Internal.group   = gnames[group_index];
     Pub.Internal.lseg    = snames[segment_index];
     Pub.Internal.frame   = frame;
     Pub.Internal.offset  = *obj_ptr.b16++;
     obj_name_length();  /* Eat type index. */
    EndIf;
  EndWhile;
 obj_next_record();
 return(True);
EndCode
#undef Pub

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                               obj_SEGDEF                                |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
bit_16 obj_SEGDEF()
BeginDeclarations
acbp_type                              acbp;
bit_32                                 address;
bit_8                                  align;
lname_entry_ptr                        class_lname;
bit_8                                  combine;
bit_32                                 length;
lseg_ptr                               lseg;
#define Lseg                           (*lseg)
bit_16                                 segment_index;
lname_entry_ptr                        segment_lname;
EndDeclarations
BeginCode
 If Current_record_header.rec_typ IsNot SEGDEF_record
  Then
   return(False);
  EndIf;
 acbp    = *obj_ptr.acbp++;
 align   = Bit_8(acbp.a);
 If align Is absolute_segment
  Then
   address  = (Bit_32(*obj_ptr.b16++) ShiftedLeft 4L);  /* Frame */
   address += Bit_32(*obj_ptr.b8++);                    /* Offset */
  Else
   address = 0L;
  EndIf;
 If align Exceeds dword_aligned
  Then
   linker_error(12, "Translator error:\n"
                    "\tModule:  \"%Fs\"\n"
                    "\t  File:  \"%Fs\"\n"
                    "\tOffset:  %lu\n"
                    "\t Error:  Align type of %u is undefined.\n",
                    (*tmodule_name).symbol,
                    (*infile.file_info).filename,
                    current_record_offset,
                    align);
  EndIf;
 combine = Bit_8(acbp.c);
 If (combine Is 4) OrIf (combine Is 7)
  Then /* Treat combine types 4 and 7 the same as 2. */
   combine = public_combine;
  EndIf;
 If (combine Is 1) OrIf (combine Is 3)
  Then /* This is a translator error. */
   linker_error(12, "Translator error:\n"
                    "\tModule:  \"%Fs\"\n"
                    "\t  File:  \"%Fs\"\n"
                    "\tOffset:  %lu\n"
                    "\t Error:  Combine type of %u is undefined.\n",
                    (*tmodule_name).symbol,
                    (*infile.file_info).filename,
                    current_record_offset,
                    combine);
  EndIf;
 length = Bit_32(*obj_ptr.b16++);
 If acbp.b IsNotZero
  Then
   If length IsNotZero
    Then
     linker_error(12, "Translator error:\n"
                      "\tModule:  \"%Fs\"\n"
                      "\t  File:  \"%Fs\"\n"
                      "\tOffset:  %lu\n"
                      "\t Error:  SEGDEF has acbp.b of 1 and length not "
                                  "zero.\n",
                      (*tmodule_name).symbol,
                      (*infile.file_info).filename,
                      current_record_offset);
    EndIf;
   length = 65536L;
  EndIf;
 segment_index         = obj_index_LNAME();
 segment_lname         = lnames[segment_index];
 class_lname           = lnames[obj_index_LNAME()];
 lseg = obj_generate_segment(segment_lname,
                             class_lname,
                             combine,
                             align,
                             tmodule_name,
                             infile.file_info,
                             address,
                             length);
 If n_segments NotLessThan max_segments.val
  Then
   linker_error(12, "Internal limit exceeded:\n"
                    "\tModule:  \"%Fs\"\n"
                    "\t  File:  \"%Fs\"\n"
                    "\tOffset:  %lu\n"
                    "\t Error:  Too many SEGDEFs.  Max of %u exceeded.\n"
                    "\t         Retry with larger \"/maxsegments:n\" "
                                "switch.\n",
                    (*tmodule_name).symbol,
                    (*infile.file_info).filename,
                    current_record_offset,
                    max_segments.val);
  EndIf;
 snames[++n_segments]  = lseg;

 obj_next_record();
 return(True);
EndCode
#undef Lseg

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                              obj_seg_grp                                |
  |                                                                         |
  +-------------------------------------------------------------------------+*/

/* obj_seg_grp:: {obj_LNAMES | obj_SEGDEF | obj_EXTDEF}
                 {obj_TYPDEF | obj_EXTDEF | obj_GRPDEF} */
bit_16 obj_seg_grp()
BeginDeclarations
EndDeclarations
BeginCode
 While obj_LNAMES() OrIf obj_SEGDEF() OrIf obj_EXTDEF()
  BeginWhile
  EndWhile;
 While obj_TYPDEF() OrIf obj_EXTDEF() OrIf obj_GRPDEF()
  BeginWhile
  EndWhile;
 return(True);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                               obj_THEADR                                |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
bit_16 obj_THEADR()
BeginDeclarations
EndDeclarations
BeginCode
 If Current_record_header.rec_typ IsNot THEADR_record
  Then
   return(False);
  EndIf;
 tmodule_name = obj_name();
 obj_next_record();
 return(True);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                             obj_thread_def                              |
  |                                                                         |
  +-------------------------------------------------------------------------+*/

/* obj_thread_def:: obj_FIXUPP  (containing only thread fields) */

bit_16 obj_thread_def()
BeginDeclarations
EndDeclarations
BeginCode
 If obj_FIXUPP()
  Then
   If FIXUPP_contains_only_threads
    Then
     return(True);
    Else
     linker_error(12, "Translator error:\n"
                      "\tModule:  \"%Fs\"\n"
                      "\t  File:  \"%Fs\"\n"
                      "\tOffset:  %lu\n"
                      "\t Error:  \"THREAD DEF\" FIXUPP encountered which "
                                  "did not contain\n"
                      "\t          only thread defs.\n",
                      (*tmodule_name).symbol,
                      (*infile.file_info).filename,
                      current_record_offset);
    EndIf;
  EndIf;
 return(False);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                              obj_tmodule                                |
  |                                                                         |
  +-------------------------------------------------------------------------+*/

/* obj_t_module:: obj_THEADR obj_seg_grp {obj_component} obj_modtail */

bit_16 obj_tmodule()
BeginDeclarations
EndDeclarations
BeginCode
 far_set(BytePtr(externals), 0,
         sizeof(public_entry_ptr)*(max_externals.val+1));
 far_set(BytePtr(gnames),    0,
         sizeof(group_entry_ptr)*(max_groups.val+1));
 far_set(BytePtr(lnames),    0,
         sizeof(lname_entry_ptr)*(max_lnames.val+1));
 far_set(BytePtr(snames),    0,
         sizeof(lseg_ptr)*(max_segments.val+1));
 far_set(BytePtr(target_thread), 0, sizeof(thread_type)*4);
 far_set(BytePtr(frame_thread),  0, sizeof(thread_type)*4);

 n_externals =
 n_groups    =
 n_lnames    =
 n_segments  = 0;
 tmodule_number++;
 tmodule_name = lookup_lname(31, (byte *) "(THEADR record not encountered)");
 obj_next_record();
 If Not obj_THEADR()
  Then
   linker_error(12, "Translator error:\n"
                    "\tModule:  \"%Fs\"\n"
                    "\t  File:  \"%Fs\"\n"
                    "\tOffset:  %lu\n"
                    "\t Error:  T-MODULE record missing.\n",
                    (*tmodule_name).symbol,
                    (*infile.file_info).filename,
                    current_record_offset);
  EndIf;
 If Not obj_seg_grp()
  Then
   linker_error(12, "Translator error:\n"
                    "\tModule:  \"%Fs\"\n"
                    "\t  File:  \"%Fs\"\n"
                    "\tOffset:  %lu\n"
                    "\t Error:  Segment/Group definition record(s) missing.\n",
                    (*tmodule_name).symbol,
                    (*infile.file_info).filename,
                    current_record_offset);
  EndIf;
 While obj_component()
  BeginWhile
  EndWhile;
 If Not obj_modtail()
  Then
   linker_error(12, "Translator error:\n"
                    "\tModule:  \"%Fs\"\n"
                    "\t  File:  \"%Fs\"\n"
                    "\tOffset:  %lu\n"
                    "\t Error:  MODEND record missing.\n",
                    (*tmodule_name).symbol,
                    (*infile.file_info).filename,
                    current_record_offset);
  EndIf;
 return(True);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                               obj_TYPDEF                                |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
bit_16 obj_TYPDEF()
BeginDeclarations
EndDeclarations
BeginCode
 If Current_record_header.rec_typ IsNot TYPDEF_record
  Then
   return(False);
  EndIf;
 obj_next_record();
 return(True);
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                              write_temp_file                            |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
void write_temp_file(bit_8           rec_typ,
                     lseg_ptr        lseg,
                     bit_16          offset,
                     byte_ptr        data,
                     bit_16          len)
BeginDeclarations
EndDeclarations
BeginCode
 temp_file_header.rec_typ       = rec_typ;
 temp_file_header.rec_len       = len;
 temp_file_header.lseg          = lseg;
 temp_file_header.offset        = offset;
 file_write(BytePtr(Addr(temp_file_header)), 
            Bit_32(sizeof(temp_file_header)));
 If len Exceeds 0
  Then
   file_write(data, Bit_32(len));
  EndIf;
 return;
EndCode
