void ObjReadData (FILE* F, ObjData* O) /* Read object file data from the given file. The function expects the Name * and Start fields to be valid. Header and basic data are read. */ { unsigned long Count; /* Seek to the start of the object file data */ fseek (F, O->Start, SEEK_SET); /* Read the object file header */ ObjReadHeader (F, &O->Header, O->Name); /* Read the string pool */ fseek (F, O->Start + O->Header.StrPoolOffs, SEEK_SET); Count = ReadVar (F); CollGrow (&O->Strings, Count); while (Count--) { CollAppend (&O->Strings, ReadStr (F)); } /* Read the exports */ fseek (F, O->Start + O->Header.ExportOffs, SEEK_SET); Count = ReadVar (F); CollGrow (&O->Exports, Count); while (Count--) { unsigned char ConDes[CD_TYPE_COUNT]; /* Skip data until we get to the name */ unsigned Type = ReadVar (F); (void) Read8 (F); /* AddrSize */ ReadData (F, ConDes, SYM_GET_CONDES_COUNT (Type)); /* Now this is what we actually need: The name of the export */ CollAppend (&O->Exports, CollAt (&O->Strings, ReadVar (F))); /* Skip the export value */ if (SYM_IS_EXPR (Type)) { /* Expression tree */ SkipExpr (F); } else { /* Literal value */ (void) Read32 (F); } /* Skip the size if necessary */ if (SYM_HAS_SIZE (Type)) { (void) ReadVar (F); } /* Line info indices */ SkipLineInfoList (F); SkipLineInfoList (F); } }
static Segment* NewSegFromDef (SegDef* Def) /* Create a new segment from a segment definition. Used only internally, no * checks. */ { /* Create a new segment */ Segment* S = xmalloc (sizeof (*S)); /* Initialize it */ S->Root = 0; S->Last = 0; S->FragCount = 0; S->Num = CollCount (&SegmentList); S->Flags = SEG_FLAG_NONE; S->Align = 1; S->RelocMode = 1; S->PC = 0; S->AbsPC = 0; S->Def = Def; /* Insert it into the segment list */ CollAppend (&SegmentList, S); /* And return it... */ return S; }
CodeLabel* CS_AddLabel (CodeSeg* S, const char* Name) /* Add a code label for the next instruction to follow */ { /* Calculate the hash from the name */ unsigned Hash = HashStr (Name) % CS_LABEL_HASH_SIZE; /* Try to find the code label if it does already exist */ CodeLabel* L = CS_FindLabel (S, Name, Hash); /* Did we find it? */ if (L) { /* We found it - be sure it does not already have an owner */ if (L->Owner) { Error ("ASM label `%s' is already defined", Name); return L; } } else { /* Not found - create a new one */ L = CS_NewCodeLabel (S, Name, Hash); } /* Safety. This call is quite costly, but safety is better */ if (CollIndex (&S->Labels, L) >= 0) { Error ("ASM label `%s' is already defined", Name); return L; } /* We do now have a valid label. Remember it for later */ CollAppend (&S->Labels, L); /* Return the label */ return L; }
Section* NewSection (Segment* Seg, unsigned long Alignment, unsigned char AddrSize) /* Create a new section for the given segment */ { /* Allocate memory */ Section* S = xmalloc (sizeof (Section)); /* Initialize the data */ S->Next = 0; S->Seg = Seg; S->Obj = 0; S->FragRoot = 0; S->FragLast = 0; S->Size = 0; S->Alignment= Alignment; S->AddrSize = AddrSize; /* Calculate the alignment bytes needed for the section */ S->Fill = AlignCount (Seg->Size, S->Alignment); /* Adjust the segment size and set the section offset */ Seg->Size += S->Fill; S->Offs = Seg->Size; /* Current size is offset */ /* Insert the section into the segment */ CollAppend (&Seg->Sections, S); /* Return the struct */ return S; }
CodeEntry* NewCodeEntry (opc_t OPC, am_t AM, const char* Arg, CodeLabel* JumpTo, LineInfo* LI) /* Create a new code entry, initialize and return it */ { /* Get the opcode description */ const OPCDesc* D = GetOPCDesc (OPC); /* Allocate memory */ CodeEntry* E = xmalloc (sizeof (CodeEntry)); /* Initialize the fields */ E->OPC = D->OPC; E->AM = AM; E->Size = GetInsnSize (E->OPC, E->AM); E->Arg = GetArgCopy (Arg); E->Flags = NumArg (E->Arg, &E->Num)? CEF_NUMARG : 0; /* Needs E->Arg */ E->Info = D->Info; E->JumpTo = JumpTo; E->LI = UseLineInfo (LI); E->RI = 0; SetUseChgInfo (E, D); InitCollection (&E->Labels); /* If we have a label given, add this entry to the label */ if (JumpTo) { CollAppend (&JumpTo->JumpFrom, E); } /* Return the initialized struct */ return E; }
static O65Data* ReadO65Data (FILE* F) /* Read a complete o65 file into dynamically allocated memory and return the * created O65Data struct. */ { unsigned long Count; O65Option* O; /* Create the struct we're going to return */ O65Data* D = NewO65Data (); /* Read the header */ ReadO65Header (F, &D->Header); /* Read the options */ while ((O = ReadO65Option (F)) != 0) { CollAppend (&D->Options, O); } /* Allocate space for the text segment and read it */ D->Text = xmalloc (D->Header.tlen); ReadData (F, D->Text, D->Header.tlen); /* Allocate space for the data segment and read it */ D->Data = xmalloc (D->Header.dlen); ReadData (F, D->Data, D->Header.dlen); /* Read the undefined references list */ Count = ReadO65Size (F, &D->Header); while (Count--) { CollAppend (&D->Imports, ReadO65Import (F)); } /* Read the relocation tables for text and data segment */ ReadO65RelocInfo (F, D, &D->TextReloc); ReadO65RelocInfo (F, D, &D->DataReloc); /* Read the exported globals list */ Count = ReadO65Size (F, &D->Header); while (Count--) { CollAppend (&D->Exports, ReadO65Export (F, &D->Header)); } /* Return the o65 data read from the file */ return D; }
void CS_AddEntry (CodeSeg* S, struct CodeEntry* E) /* Add an entry to the given code segment */ { /* Transfer the labels if we have any */ CS_MoveLabelsToEntry (S, E); /* Add the entry to the list of code entries in this segment */ CollAppend (&S->Entries, E); }
static void AddAttr (Declaration* D, DeclAttr* A) /* Add an attribute to a declaration */ { /* Allocate the list if necessary, the add the attribute */ if (D->Attributes == 0) { D->Attributes = NewCollection (); } CollAppend (D->Attributes, A); }
void SymRef (SymEntry* S) /* Mark the given symbol as referenced */ { /* Mark the symbol as referenced */ S->Flags |= SF_REFERENCED; /* Remember the current location */ CollAppend (&S->RefLines, GetAsmLineInfo ()); }
void CE_AttachLabel (CodeEntry* E, CodeLabel* L) /* Attach the label to the entry */ { /* Add it to the entries label list */ CollAppend (&E->Labels, L); /* Tell the label about it's owner */ L->Owner = E; }
static AFile* NewAFile (IFile* IF, FILE* F) /* Create a new AFile, push it onto the stack, add the path of the file to * the path search list, and finally return a pointer to the new AFile struct. */ { StrBuf Path = AUTO_STRBUF_INITIALIZER; /* Allocate a AFile structure */ AFile* AF = (AFile*) xmalloc (sizeof (AFile)); /* Initialize the fields */ AF->Line = 0; AF->F = F; AF->Input = IF; /* Increment the usage counter of the corresponding IFile. If this * is the first use, set the file data and output debug info if * requested. */ if (IF->Usage++ == 0) { /* Get file size and modification time. There a race condition here, * since we cannot use fileno() (non standard identifier in standard * header file), and therefore not fstat. When using stat with the * file name, there's a risk that the file was deleted and recreated * while it was open. Since mtime and size are only used to check * if a file has changed in the debugger, we will ignore this problem * here. */ struct stat Buf; if (FileStat (IF->Name, &Buf) != 0) { /* Error */ Fatal ("Cannot stat `%s': %s", IF->Name, strerror (errno)); } IF->Size = (unsigned long) Buf.st_size; IF->MTime = (unsigned long) Buf.st_mtime; /* Set the debug data */ g_fileinfo (IF->Name, IF->Size, IF->MTime); } /* Insert the new structure into the AFile collection */ CollAppend (&AFiles, AF); /* Get the path of this file and add it as an extra search path. * To avoid file search overhead, we will add one path only once. * This is checked by the PushSearchPath function. */ SB_CopyBuf (&Path, IF->Name, FindName (IF->Name) - IF->Name); SB_Terminate (&Path); AF->SearchPath = PushSearchPath (UsrIncSearchPath, SB_GetConstBuf (&Path)); SB_Done (&Path); /* Return the new struct */ return AF; }
void CE_MoveLabel (CodeLabel* L, CodeEntry* E) /* Move the code label L from it's former owner to the code entry E. */ { /* Delete the label from the owner */ CollDeleteItem (&L->Owner->Labels, L); /* Set the new owner */ CollAppend (&E->Labels, L); L->Owner = E; }
void ReadStrPool (FILE* F, Collection* C) /* Read a string pool from the current position into C. */ { /* The number of strings is the first item */ unsigned long Count = ReadVar (F); /* Read all the strings into C */ while (Count--) { CollAppend (C, ReadStr (F)); } }
static void CS_MoveLabelsToPool (CodeSeg* S, CodeEntry* E) /* Move the labels of the code entry E to the label pool of the code segment */ { unsigned LabelCount = CE_GetLabelCount (E); while (LabelCount--) { CodeLabel* L = CE_GetLabel (E, LabelCount); L->Owner = 0; CollAppend (&S->Labels, L); } CollDeleteAll (&E->Labels); }
Segments* PushSegments (SymEntry* Func) /* Make the new segment list current but remember the old one */ { /* Push the current pointer onto the stack */ CollAppend (&SegmentStack, CS); /* Create a new Segments structure */ CS = NewSegments (Func); /* Return the new struct */ return CS; }
static void ME_AppendActual (MacroExp* E, StrBuf* Arg) /* Add a copy of Arg to the list of actual macro arguments. * NOTE: This function will clear Arg! */ { /* Create a new string buffer */ StrBuf* A = NewStrBuf (); /* Move the contents of Arg to A */ SB_Move (A, Arg); /* Add A to the actual arguments */ CollAppend (&E->ActualArgs, A); }
static ULabel* NewULabel (ExprNode* Val) /* Create a new ULabel and insert it into the collection. The created label * structure is returned. */ { /* Allocate memory for the ULabel structure */ ULabel* L = xmalloc (sizeof (ULabel)); /* Initialize the fields */ L->LineInfos = EmptyCollection; GetFullLineInfo (&L->LineInfos); L->Val = Val; L->Ref = 0; /* Insert the label into the collection */ CollAppend (&ULabList, L); /* Return the created label */ return L; }
Assertion* ReadAssertion (FILE* F, struct ObjData* O) /* Read an assertion from the given file */ { /* Allocate memory */ Assertion* A = xmalloc (sizeof (Assertion)); /* Read the fields from the file */ A->LineInfos = EmptyCollection; A->Expr = ReadExpr (F, O); A->Action = (AssertAction) ReadVar (F); A->Msg = MakeGlobalStringId (O, ReadVar (F)); ReadLineInfoList (F, O, &A->LineInfos); /* Set remaining fields */ A->Obj = O; /* Add the assertion to the global list */ CollAppend (&Assertions, A); /* Return the new struct */ return A; }
ObjData* NewObjData (void) /* Allocate a new structure on the heap, insert it into the list, return it */ { /* Allocate memory */ ObjData* O = xmalloc (sizeof (ObjData)); /* Initialize the data */ O->Name = 0; O->Flags = 0; O->MTime = 0; O->Start = 0; O->Size = 0; O->Strings = EmptyCollection; O->Exports = EmptyCollection; /* Add it to the list */ CollAppend (&ObjPool, O); /* Return the new entry */ return O; }
static IFile* NewIFile (const char* Name, InputType Type) /* Create and return a new IFile */ { /* Get the length of the name */ unsigned Len = strlen (Name); /* Allocate a IFile structure */ IFile* IF = (IFile*) xmalloc (sizeof (IFile) + Len); /* Initialize the fields */ IF->Index = CollCount (&IFiles) + 1; IF->Usage = 0; IF->Size = 0; IF->MTime = 0; IF->Type = Type; memcpy (IF->Name, Name, Len+1); /* Insert the new structure into the IFile collection */ CollAppend (&IFiles, IF); /* Return the new struct */ return IF; }
static Segment* NewSegment (unsigned Name, unsigned char AddrSize) /* Create a new segment and initialize it */ { unsigned Hash; /* Allocate memory */ Segment* S = xmalloc (sizeof (Segment)); /* Initialize the fields */ S->Name = Name; S->Next = 0; S->Flags = SEG_FLAG_NONE; S->Sections = EmptyCollection; S->MemArea = 0; S->PC = 0; S->Size = 0; S->OutputName = 0; S->OutputOffs = 0; S->Alignment = 1; S->FillVal = 0; S->AddrSize = AddrSize; S->ReadOnly = 0; S->Dumped = 0; /* Insert the segment into the segment list and assign the segment id */ S->Id = CollCount (&SegmentList); CollAppend (&SegmentList, S); /* Insert the segment into the segment hash list */ Hash = (S->Name & HASHTAB_MASK); S->Next = HashTab[Hash]; HashTab[Hash] = S; /* Return the new entry */ return S; }
static FileEntry* NewFileEntry (unsigned Name, FileType Type, unsigned long Size, unsigned long MTime) /* Create a new FileEntry, insert it into the tables and return it */ { /* Allocate memory for the entry */ FileEntry* F = xmalloc (sizeof (FileEntry)); /* Initialize the fields */ InitHashNode (&F->Node); F->Name = Name; F->Index = CollCount (&FileTab) + 1; /* First file has index #1 */ F->Type = Type; F->Size = Size; F->MTime = MTime; /* Insert the file into the file table */ CollAppend (&FileTab, F); /* Insert the entry into the hash table */ HT_Insert (&HashTab, F); /* Return the new entry */ return F; }
void AddAssertion (ExprNode* Expr, AssertAction Action, unsigned Msg) /* Add an assertion to the assertion table */ { /* Add an assertion object to the table */ CollAppend (&Assertions, NewAssertion (Expr, Action, Msg)); }
void SymConDes (SymEntry* S, unsigned char AddrSize, unsigned Type, unsigned Prio) /* Mark the given symbol as a module constructor/destructor. This will also ** mark the symbol as an export. Initializers may never be zero page symbols. */ { /* Check the parameters */ #if (CD_TYPE_MIN != 0) CHECK (Type >= CD_TYPE_MIN && Type <= CD_TYPE_MAX); #else CHECK (Type <= CD_TYPE_MAX); #endif CHECK (Prio >= CD_PRIO_MIN && Prio <= CD_PRIO_MAX); /* Check for errors */ if (S->Flags & SF_IMPORT) { /* The symbol is already marked as imported external symbol */ Error ("Symbol `%m%p' is already an import", GetSymName (S)); return; } if (S->Flags & SF_VAR) { /* Variable symbols cannot be exported or imported */ Error ("Var symbol `%m%p' cannot be exported", GetSymName (S)); return; } /* If the symbol was already marked as an export or global, check if ** this was done specifiying the same address size. In case of a global ** declaration, silently remove the global flag. */ if (S->Flags & (SF_EXPORT | SF_GLOBAL)) { if (S->ExportSize != AddrSize) { Error ("Address size mismatch for symbol `%m%p'", GetSymName (S)); } S->Flags &= ~SF_GLOBAL; } S->ExportSize = AddrSize; /* If the symbol is already defined, check symbol size against the ** exported size. */ if (S->Flags & SF_DEFINED) { if (S->ExportSize == ADDR_SIZE_DEFAULT) { /* Use the real size of the symbol */ S->ExportSize = S->AddrSize; } else if (S->AddrSize != S->ExportSize) { Error ("Address size mismatch for symbol `%m%p'", GetSymName (S)); } } /* If the symbol already was declared as a condes of this type, ** check if the new priority value is the same as the old one. */ if (S->ConDesPrio[Type] != CD_PRIO_NONE) { if (S->ConDesPrio[Type] != Prio) { Error ("Redeclaration mismatch for symbol `%m%p'", GetSymName (S)); } } S->ConDesPrio[Type] = Prio; /* Set the symbol data */ S->Flags |= (SF_EXPORT | SF_REFERENCED); /* Remember the line info for this reference */ CollAppend (&S->RefLines, GetAsmLineInfo ()); }
Export* ReadExport (FILE* F, ObjData* O) /* Read an export from a file */ { unsigned ConDesCount; unsigned I; Export* E; /* Read the type */ unsigned Type = ReadVar (F); /* Read the address size */ unsigned char AddrSize = Read8 (F); /* Create a new export without a name */ E = NewExport (Type, AddrSize, INVALID_STRING_ID, O); /* Read the constructor/destructor decls if we have any */ ConDesCount = SYM_GET_CONDES_COUNT (Type); if (ConDesCount > 0) { unsigned char ConDes[CD_TYPE_COUNT]; /* Read the data into temp storage */ ReadData (F, ConDes, ConDesCount); /* Re-order the data. In the file, each decl is encoded into a byte ** which contains the type and the priority. In memory, we will use ** an array of types which contain the priority. */ for (I = 0; I < ConDesCount; ++I) { E->ConDes[CD_GET_TYPE (ConDes[I])] = CD_GET_PRIO (ConDes[I]); } } /* Read the name */ E->Name = MakeGlobalStringId (O, ReadVar (F)); /* Read the value */ if (SYM_IS_EXPR (Type)) { E->Expr = ReadExpr (F, O); } else { E->Expr = LiteralExpr (Read32 (F), O); } /* Read the size */ if (SYM_HAS_SIZE (Type)) { E->Size = ReadVar (F); } /* Last are the locations */ ReadLineInfoList (F, O, &E->DefLines); ReadLineInfoList (F, O, &E->RefLines); /* If this symbol is exported as a condes, and the condes type declares a ** forced import, add this import to the object module. */ for (I = 0; I < CD_TYPE_COUNT; ++I) { const ConDesImport* CDI; if (E->ConDes[I] != CD_PRIO_NONE && (CDI = ConDesGetImport (I)) != 0) { unsigned J; /* Generate a new import, and add it to the module's import list. */ Import* Imp = GenImport (CDI->Name, CDI->AddrSize); Imp->Obj = O; CollAppend (&O->Imports, Imp); /* Add line info for the export that is actually the condes that ** forces the import. Then, add line info for the config. file. ** The export's info is added first because the import pretends ** that it came from the object module instead of the config. file. */ for (J = 0; J < CollCount (&E->DefLines); ++J) { CollAppend (&Imp->RefLines, DupLineInfo (CollAt (&E->DefLines, J))); } CollAppend (&Imp->RefLines, GenLineInfo (&CDI->Pos)); } } /* Return the new export */ return E; }
int CE_UseLoadFlags (CodeEntry* E) /* Return true if the instruction uses any flags that are set by a load of ** a register (N and Z). */ { /* Follow unconditional branches, but beware of endless loops. After this, ** E will point to the first entry that is not a branch. */ if (E->Info & OF_UBRA) { Collection C = AUTO_COLLECTION_INITIALIZER; /* Follow the chain */ while (E->Info & OF_UBRA) { /* Remember the entry so we can detect loops */ CollAppend (&C, E); /* Check the target */ if (E->JumpTo == 0 || CollIndex (&C, E->JumpTo->Owner) >= 0) { /* Unconditional jump to external symbol, or endless loop. */ DoneCollection (&C); return 0; /* Flags not used */ } /* Follow the chain */ E = E->JumpTo->Owner; } /* Delete the collection */ DoneCollection (&C); } /* A branch will use the flags */ if (E->Info & OF_FBRA) { return 1; } /* Call of a boolean transformer routine will also use the flags */ if (E->OPC == OP65_JSR) { /* Get the condition that is evaluated and check it */ switch (FindBoolCmpCond (E->Arg)) { case CMP_EQ: case CMP_NE: case CMP_GT: case CMP_GE: case CMP_LT: case CMP_LE: case CMP_UGT: case CMP_ULE: /* Will use the N or Z flags */ return 1; case CMP_UGE: /* Uses only carry */ case CMP_ULT: /* Dito */ default: /* No bool transformer subroutine */ return 0; } } /* Anything else */ return 0; }
static void Add (SearchPaths* P, const char* New) /* Cleanup a new search path and add it to the list */ { /* Add a clean copy of the path to the collection */ CollAppend (P, CleanupPath (New)); }
void InsertObjData (ObjData* O) /* Insert the ObjData object into the collection of used ObjData objects. */ { CollAppend (&ObjDataList, O); }
static unsigned GetRegInfo2 (CodeSeg* S, CodeEntry* E, int Index, Collection* Visited, unsigned Used, unsigned Unused, unsigned Wanted) /* Recursively called subfunction for GetRegInfo. */ { /* Follow the instruction flow recording register usage. */ while (1) { unsigned R; /* Check if we have already visited the current code entry. If so, ** bail out. */ if (CE_HasMark (E)) { break; } /* Mark this entry as already visited */ CE_SetMark (E); CollAppend (Visited, E); /* Evaluate the used registers */ R = E->Use; if (E->OPC == OP65_RTS || ((E->Info & OF_UBRA) != 0 && E->JumpTo == 0)) { /* This instruction will leave the function */ R |= S->ExitRegs; } if (R != REG_NONE) { /* We are not interested in the use of any register that has been ** used before. */ R &= ~Unused; /* Remember the remaining registers */ Used |= R; } /* Evaluate the changed registers */ if ((R = E->Chg) != REG_NONE) { /* We are not interested in the use of any register that has been ** used before. */ R &= ~Used; /* Remember the remaining registers */ Unused |= R; } /* If we know about all registers now, bail out */ if (((Used | Unused) & Wanted) == Wanted) { break; } /* If the instruction is an RTS or RTI, we're done */ if ((E->Info & OF_RET) != 0) { break; } /* If we have an unconditional branch, follow this branch if possible, ** otherwise we're done. */ if ((E->Info & OF_UBRA) != 0) { /* Does this jump have a valid target? */ if (E->JumpTo) { /* Unconditional jump */ E = E->JumpTo->Owner; Index = -1; /* Invalidate */ } else { /* Jump outside means we're done */ break; } /* In case of conditional branches, follow the branch if possible and ** follow the normal flow (branch not taken) afterwards. If we cannot ** follow the branch, we're done. */ } else if ((E->Info & OF_CBRA) != 0) { /* Recursively determine register usage at the branch target */ unsigned U1; unsigned U2; if (E->JumpTo) { /* Jump to internal label */ U1 = GetRegInfo2 (S, E->JumpTo->Owner, -1, Visited, Used, Unused, Wanted); } else { /* Jump to external label. This will effectively exit the ** function, so we use the exitregs information here. */ U1 = S->ExitRegs; } /* Get the next entry */ if (Index < 0) { Index = CS_GetEntryIndex (S, E); } if ((E = CS_GetEntry (S, ++Index)) == 0) { Internal ("GetRegInfo2: No next entry!"); } /* Follow flow if branch not taken */ U2 = GetRegInfo2 (S, E, Index, Visited, Used, Unused, Wanted); /* Registers are used if they're use in any of the branches */ return U1 | U2; } else { /* Just go to the next instruction */ if (Index < 0) { Index = CS_GetEntryIndex (S, E); } E = CS_GetEntry (S, ++Index); if (E == 0) { /* No next entry */ Internal ("GetRegInfo2: No next entry!"); } } } /* Return to the caller the complement of all unused registers */ return Used; }
static void ReadO65RelocInfo (FILE* F, const O65Data* D, Collection* Reloc) /* Read relocation data for one segment */ { /* Relocation starts at (start address - 1) */ unsigned long Offs = (unsigned long) -1L; while (1) { O65Reloc* R; /* Read the next relocation offset */ unsigned char C = Read8 (F); if (C == 0) { /* End of relocation table */ break; } /* Create a new relocation entry */ R = xmalloc (sizeof (*R)); /* Handle overflow bytes */ while (C == 0xFF) { Offs += 0xFE; C = Read8 (F); } /* Calculate the final offset */ R->Offs = (Offs += C); /* Read typebyte and segment id */ C = Read8 (F); R->Type = (C & O65_RTYPE_MASK); R->SegID = (C & O65_SEGID_MASK); /* Read an additional relocation value if there is one */ R->SymIdx = (R->SegID == O65_SEGID_UNDEF)? ReadO65Size (F, &D->Header) : 0; switch (R->Type) { case O65_RTYPE_HIGH: if ((D->Header.mode & O65_RELOC_MASK) == O65_RELOC_BYTE) { /* Low byte follows */ R->Val = Read8 (F); } else { /* Low byte is zero */ R->Val = 0; } break; case O65_RTYPE_SEG: /* Low 16 byte of the segment address follow */ R->Val = Read16 (F); break; default: R->Val = 0; break; } /* Insert this relocation entry into the collection */ CollAppend (Reloc, R); } }