static void GetInputChar (void) /* Read the next character from the input stream and make CurC and NextC * valid. If end of line is reached, both are set to NUL, no more lines * are read by this function. */ { /* Drop all pushed fragments that don't have data left */ while (SB_GetIndex (Line) >= SB_GetLen (Line)) { /* Cannot read more from this line, check next line on stack if any */ if (CollCount (&InputStack) == 0) { /* This is THE line */ break; } FreeStrBuf (Line); Line = CollPop (&InputStack); } /* Now get the next characters from the line */ if (SB_GetIndex (Line) >= SB_GetLen (Line)) { CurC = NextC = '\0'; } else { CurC = SB_AtUnchecked (Line, SB_GetIndex (Line)); if (SB_GetIndex (Line) + 1 < SB_GetLen (Line)) { /* NextC comes from this fragment */ NextC = SB_AtUnchecked (Line, SB_GetIndex (Line) + 1); } else { /* NextC comes from next fragment */ if (CollCount (&InputStack) > 0) { NextC = ' '; } else { NextC = '\0'; } } } }
static unsigned GetRegInfo1 (CodeSeg* S, CodeEntry* E, int Index, Collection* Visited, unsigned Used, unsigned Unused, unsigned Wanted) /* Recursively called subfunction for GetRegInfo. */ { /* Remember the current count of the line collection */ unsigned Count = CollCount (Visited); /* Call the worker routine */ unsigned R = GetRegInfo2 (S, E, Index, Visited, Used, Unused, Wanted); /* Restore the old count, unmarking all new entries */ unsigned NewCount = CollCount (Visited); while (NewCount-- > Count) { CodeEntry* E = CollAt (Visited, NewCount); CE_ResetMark (E); CollDelete (Visited, NewCount); } /* Return the registers used */ return R; }
void SegDump (void) /* Dump the segments and it's contents */ { unsigned I, J; unsigned long Count; unsigned char* Data; for (I = 0; I < CollCount (&SegmentList); ++I) { Segment* Seg = CollAtUnchecked (&SegmentList, I); printf ("Segment: %s (%lu)\n", GetString (Seg->Name), Seg->Size); for (J = 0; J < CollCount (&Seg->Sections); ++J) { Section* S = CollAtUnchecked (&Seg->Sections, J); unsigned J; Fragment* F = S->FragRoot; printf (" Section:\n"); while (F) { switch (F->Type) { case FRAG_LITERAL: printf (" Literal (%u bytes):", F->Size); Count = F->Size; Data = F->LitBuf; J = 100; while (Count--) { if (J > 75) { printf ("\n "); J = 3; } printf (" %02X", *Data++); J += 3; } printf ("\n"); break; case FRAG_EXPR: printf (" Expression (%u bytes):\n", F->Size); printf (" "); DumpExpr (F->Expr, 0); break; case FRAG_SEXPR: printf (" Signed expression (%u bytes):\n", F->Size); printf (" "); DumpExpr (F->Expr, 0); break; case FRAG_FILL: printf (" Empty space (%u bytes)\n", F->Size); break; default: Internal ("Invalid fragment type: %02X", F->Type); } F = F->Next; } } } }
static void ExecCmd (Collection* Args, const CmdEntry* Tab, unsigned Count) /* Search for the command in slot 0 of the given collection. If found, check * the argument count, then execute it. If there are problems, output a * diagnostic. */ { /* Search for the command, check number of args, then execute it */ const char* Cmd = CollAt (Args, 0); const CmdEntry* E = FindCmd (Cmd, Tab, Count); if (E == 0) { PrintLine ("No such command: %s", Cmd); return; } /* Check the number of arguments. Zero means that the function will check * itself. A negative count means that the function needs at least * abs(count) arguments. A positive count means that the function needs * exactly this number of arguments. * Note: The number includes the command itself. */ if (E->ArgCount > 0 && (int)CollCount (Args) != E->ArgCount) { /* Argument number mismatch */ switch (E->ArgCount) { case 1: PrintLine ("Command doesn't accept an argument"); return; case 2: PrintLine ("Command requires an argument"); return; default: PrintLine ("Command requires %d arguments", E->ArgCount-1); return; } } else if (E->ArgCount < 0 && (int)CollCount (Args) < -E->ArgCount) { /* Argument number mismatch */ switch (E->ArgCount) { case -2: PrintLine ("Command requires at least one argument"); return; default: PrintLine ("Command requires at least %d arguments", E->ArgCount-1); return; } } else { /* Remove the command from the argument list, then execute it */ CollDelete (Args, 0); E->Func (Args); } }
static void PrintO65Stats (const O65Data* D) /* Print information about the O65 file if --verbose is given */ { Print (stdout, 1, "Size of text segment: %5lu\n", D->Header.tlen); Print (stdout, 1, "Size of data segment: %5lu\n", D->Header.dlen); Print (stdout, 1, "Size of bss segment: %5lu\n", D->Header.blen); Print (stdout, 1, "Size of zeropage segment: %5lu\n", D->Header.zlen); Print (stdout, 1, "Number of imports: %5u\n", CollCount (&D->Imports)); Print (stdout, 1, "Number of exports: %5u\n", CollCount (&D->Exports)); Print (stdout, 1, "Number of text segment relocations: %5u\n", CollCount (&D->TextReloc)); Print (stdout, 1, "Number of data segment relocations: %5u\n", CollCount (&D->DataReloc)); }
ExprNode* ULabRef (int Which) /* Get an unnamed label. If Which is negative, it is a backreference (a * reference to an already defined label), and the function will return a * segment relative expression. If Which is positive, it is a forward ref, * and the function will return a expression node for an unnamed label that * must be resolved later. */ { int Index; ULabel* L; /* Which can never be 0 */ PRECONDITION (Which != 0); /* Get the index of the referenced label */ if (Which > 0) { --Which; } Index = (int) ULabDefCount + Which; /* We cannot have negative label indices */ if (Index < 0) { /* Label does not exist */ Error ("Undefined label"); /* We must return something valid */ return GenCurrentPC(); } /* Check if the label exists. If not, generate enough forward labels. */ if (Index < (int) CollCount (&ULabList)) { /* The label exists, get it. */ L = CollAtUnchecked (&ULabList, Index); } else { /* Generate new, undefined labels */ while (Index >= (int) CollCount (&ULabList)) { L = NewULabel (0); } } /* Mark the label as referenced */ ++L->Ref; /* If the label is already defined, return its value, otherwise return * just a reference. */ if (L->Val) { return CloneExpr (L->Val); } else { return GenULabelExpr (Index); } }
void InsertObjGlobals (ObjData* O) /* Insert imports and exports from the object file into the global import and * export lists. */ { unsigned I; /* Insert exports and imports */ for (I = 0; I < CollCount (&O->Exports); ++I) { InsertExport (CollAt (&O->Exports, I)); } for (I = 0; I < CollCount (&O->Imports); ++I) { InsertImport (CollAt (&O->Imports, I)); } }
Collection* ParseAttrList (const char* List, const char** NameList, unsigned NameCount) /* Parse a list containing name/value pairs into a sorted collection. Some * attributes may not need a name, so NameList contains these names. If there * were no errors, the function returns a alphabetically sorted collection * containing Attr entries. */ { const char* Name; /* Create a new collection */ Collection* C = NewCollection (); /* Name/value pairs are separated by commas */ const char* L = List; StrBuf B = AUTO_STRBUF_INITIALIZER; while (1) { if (*L == ',' || *L == ':' || *L == '\0') { /* Terminate the string */ SB_Terminate (&B); /* Determine the default name */ if (CollCount (C) >= NameCount) { Name = 0; } else { Name = NameList[CollCount (C)]; } /* Split and add this attribute/value pair */ SplitAddAttr (C, SB_GetConstBuf (&B), Name); /* Done, clear the buffer. */ SB_Clear (&B); if (*L == '\0') { break; } } else { SB_AppendChar (&B, *L); } ++L; } /* Free memory */ SB_Done (&B); /* Return the collection with the attributes */ return C; }
static void CloseIncludeFile (void) /* Close an include file and switch to the higher level file. Set Input to * NULL if this was the main file. */ { AFile* Input; /* Get the number of active input files */ unsigned AFileCount = CollCount (&AFiles); /* Must have an input file when called */ PRECONDITION (AFileCount > 0); /* Get the current active input file */ Input = (AFile*) CollLast (&AFiles); /* Close the current input file (we're just reading so no error check) */ fclose (Input->F); /* Delete the last active file from the active file collection */ CollDelete (&AFiles, AFileCount-1); /* If we had added an extra search path for this AFile, remove it */ if (Input->SearchPath) { PopSearchPath (UsrIncSearchPath); } /* Delete the active file structure */ FreeAFile (Input); }
void CS_DelLabel (CodeSeg* S, CodeLabel* L) /* Remove references from this label and delete it. */ { unsigned Count, I; /* First, remove the label from the hash chain */ CS_RemoveLabelFromHash (S, L); /* Remove references from insns jumping to this label */ Count = CollCount (&L->JumpFrom); for (I = 0; I < Count; ++I) { /* Get the insn referencing this label */ CodeEntry* E = CollAt (&L->JumpFrom, I); /* Remove the reference */ CE_ClearJumpTo (E); } CollDeleteAll (&L->JumpFrom); /* Remove the reference to the owning instruction if it has one. The * function may be called for a label without an owner when deleting * unfinished parts of the code. This is unfortunate since it allows * errors to slip through. */ if (L->Owner) { CollDeleteItem (&L->Owner->Labels, L); } /* All references removed, delete the label itself */ FreeCodeLabel (L); }
int IsBSSType (Segment* S) /* Check if the given segment is a BSS style segment, that is, it does not ** contain non-zero data. */ { /* Loop over all sections */ unsigned I; for (I = 0; I < CollCount (&S->Sections); ++I) { /* Get the next section */ Section* Sec = CollAtUnchecked (&S->Sections, I); /* Loop over all fragments */ Fragment* F = Sec->FragRoot; while (F) { if (F->Type == FRAG_LITERAL) { unsigned char* Data = F->LitBuf; unsigned long Count = F->Size; while (Count--) { if (*Data++ != 0) { return 0; } } } else if (F->Type == FRAG_EXPR || F->Type == FRAG_SEXPR) { if (GetExprVal (F->Expr) != 0) { return 0; } } F = F->Next; } } return 1; }
void WriteAssertions (void) /* Write the assertion table to the object file */ { unsigned I; /* Get the number of assertions */ unsigned Count = CollCount (&Assertions); /* Tell the object file module that we're about to start the assertions */ ObjStartAssertions (); /* Write the string count to the list */ ObjWriteVar (Count); /* Write the assertions */ for (I = 0; I < Count; ++I) { /* Get the next assertion */ Assertion* A = CollAtUnchecked (&Assertions, I); /* Write it to the file */ WriteExpr (A->Expr); ObjWriteVar ((unsigned) A->Action); ObjWriteVar (A->Msg); WriteLineInfo (&A->LI); } /* Done writing the assertions */ ObjEndAssertions (); }
static void SymReplaceExprRefs (SymEntry* S) /* Replace the references to this symbol by a copy of the symbol expression */ { unsigned I; long Val; /* Check if the expression is const and get its value */ int IsConst = IsConstExpr (S->Expr, &Val); CHECK (IsConst); /* Loop over all references */ for (I = 0; I < CollCount (&S->ExprRefs); ++I) { /* Get the expression node */ ExprNode* E = CollAtUnchecked (&S->ExprRefs, I); /* Safety */ CHECK (E->Op == EXPR_SYMBOL && E->V.Sym == S); /* We cannot touch the root node, since there are pointers to it. ** Replace it by a literal node. */ E->Op = EXPR_LITERAL; E->V.IVal = Val; } /* Remove all symbol references from the symbol */ CollDeleteAll (&S->ExprRefs); }
static void ReadIndex (void) /* Read the index of a library file */ { unsigned Count, I; /* Seek to the start of the index */ fseek (Lib, Header.IndexOffs, SEEK_SET); /* Read the object file count and calculate the cross ref size */ Count = ReadVar (Lib); /* Read all entries in the index */ while (Count--) { ReadIndexEntry (); } /* Read basic object file data from the actual entries */ for (I = 0; I < CollCount (&ObjPool); ++I) { /* Get the object file entry */ ObjData* O = CollAtUnchecked (&ObjPool, I); /* Read data */ ObjReadData (Lib, O); } }
char* SearchFile (const SearchPaths* P, const char* File) /* Search for a file in a list of directories. Return a pointer to a malloced ** area that contains the complete path, if found, return 0 otherwise. */ { char* Name = 0; StrBuf PathName = AUTO_STRBUF_INITIALIZER; /* Start the search */ unsigned I; for (I = 0; I < CollCount (P); ++I) { /* Copy the next path element into the buffer */ SB_CopyStr (&PathName, CollConstAt (P, I)); /* Add a path separator and the filename */ if (SB_NotEmpty (&PathName)) { SB_AppendChar (&PathName, '/'); } SB_AppendStr (&PathName, File); SB_Terminate (&PathName); /* Check if this file exists */ if (access (SB_GetBuf (&PathName), 0) == 0) { /* The file exists, we're done */ Name = xstrdup (SB_GetBuf (&PathName)); break; } } /* Cleanup and return the result of the search */ SB_Done (&PathName); return Name; }
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; }
void PrintDbgModules (FILE* F) /* Output the modules to a debug info file */ { unsigned I; /* Output modules */ for (I = 0; I < CollCount (&ObjDataList); ++I) { /* Get this object file */ const ObjData* O = CollConstAt (&ObjDataList, I); /* The main source file is the one at index zero */ const FileInfo* Source = CollConstAt (&O->Files, 0); /* Output the module line */ fprintf (F, "mod\tid=%u,name=\"%s\",file=%u", I, GetObjFileName (O), Source->Id); /* Add library if any */ if (O->Lib != 0) { fprintf (F, ",lib=%u", GetLibId (O->Lib)); } /* Terminate the output line */ fputc ('\n', F); } }
static void WriteDep (FILE* F, FileType Types) /* Helper function. Writes all file names that match Types to the output */ { unsigned I; /* Loop over all files */ for (I = 0; I < CollCount (&FileTab); ++I) { const StrBuf* Filename; /* Get the next input file */ const FileEntry* E = (const FileEntry*) CollAt (&FileTab, I); /* Ignore it if it is not of the correct type */ if ((E->Type & Types) == 0) { continue; } /* If this is not the first file, add a space */ if (I > 0) { fputc (' ', F); } /* Print the dependency */ Filename = GetStrBuf (E->Name); fprintf (F, "%*s", SB_GetLen (Filename), SB_GetConstBuf (Filename)); } }
void SymLeaveLevel (void) /* Leave the current lexical level */ { /* If this is a scope that allows to emit data into segments, close the ** open the spans. */ if (CurrentScope->Type <= SCOPE_HAS_DATA) { CloseSpanList (&CurrentScope->Spans); } /* If we have spans, the first one is the segment that was active, when the ** scope was opened. Set the size of the scope to the number of data bytes ** emitted into this segment. If we have an owner symbol set the size of ** this symbol, too. */ if (CollCount (&CurrentScope->Spans) > 0) { const Span* S = CollAtUnchecked (&CurrentScope->Spans, 0); unsigned long Size = GetSpanSize (S); DefSizeOfScope (CurrentScope, Size); if (CurrentScope->Label) { DefSizeOfSymbol (CurrentScope->Label, Size); } } /* Mark the scope as closed */ CurrentScope->Flags |= ST_CLOSED; /* Leave the scope */ CurrentScope = CurrentScope->Parent; }
void UseSeg (const SegDef* D) /* Use the segment with the given name */ { unsigned I; for (I = 0; I < CollCount (&SegmentList); ++I) { Segment* Seg = CollAtUnchecked (&SegmentList, I); if (strcmp (Seg->Def->Name, D->Name) == 0) { /* We found this segment. Check if the type is identical */ if (D->AddrSize != ADDR_SIZE_DEFAULT && Seg->Def->AddrSize != D->AddrSize) { Error ("Segment attribute mismatch"); /* Use the new attribute to avoid errors */ Seg->Def->AddrSize = D->AddrSize; } ActiveSeg = Seg; return; } } /* Segment is not in list, create a new one */ if (D->AddrSize == ADDR_SIZE_DEFAULT) { ActiveSeg = NewSegment (D->Name, ADDR_SIZE_ABS); } else { ActiveSeg = NewSegment (D->Name, D->AddrSize); } }
static void PrintUnresolved (ExpCheckFunc F, void* Data) /* Print a list of unresolved symbols. On unresolved symbols, F is ** called (see the comments on ExpCheckFunc in the data section). */ { unsigned I; /* Print all open imports */ for (I = 0; I < ExpCount; ++I) { Export* E = ExpPool [I]; if (E->Expr == 0 && E->ImpCount > 0 && F (E->Name, Data) == 0) { /* Unresolved external */ Import* Imp = E->ImpList; fprintf (stderr, "Unresolved external `%s' referenced in:\n", GetString (E->Name)); while (Imp) { unsigned J; for (J = 0; J < CollCount (&Imp->RefLines); ++J) { const LineInfo* LI = CollConstAt (&Imp->RefLines, J); fprintf (stderr, " %s(%u)\n", GetSourceName (LI), GetSourceLine (LI)); } Imp = Imp->Next; } } } }
static void WriteDep (FILE* F, InputType Types) /* Helper function. Writes all file names that match Types to the output */ { unsigned I; /* Loop over all files */ unsigned FileCount = CollCount (&IFiles); for (I = 0; I < FileCount; ++I) { /* Get the next input file */ const IFile* IF = (const IFile*) CollAt (&IFiles, I); /* Ignore it if it is not of the correct type */ if ((IF->Type & Types) == 0) { continue; } /* If this is not the first file, add a space */ if (I > 0) { fputc (' ', F); } /* Print the dependency escaping spaces */ WriteEscaped (F, IF->Name); } }
static void AddNotifications (const Collection* LineInfos) /* Output additional notifications for an error or warning */ { unsigned I; unsigned Output; unsigned Skipped; /* The basic line info is always in slot zero. It has been used to ** output the actual error or warning. The following slots may contain ** more information. Check them and print additional notifications if ** they're present, but limit the number to a reasonable value. */ for (I = 1, Output = 0, Skipped = 0; I < CollCount (LineInfos); ++I) { /* Get next line info */ const LineInfo* LI = CollConstAt (LineInfos, I); /* Check the type and output an appropriate note */ const char* Msg; switch (GetLineInfoType (LI)) { case LI_TYPE_ASM: Msg = "Expanded from here"; break; case LI_TYPE_EXT: Msg = "Assembler code generated from this line"; break; case LI_TYPE_MACRO: Msg = "Macro was defined here"; break; case LI_TYPE_MACPARAM: Msg = "Macro parameter came from here"; break; default: /* No output */ Msg = 0; break; } /* Output until an upper limit of messages is reached */ if (Msg) { if (Output < MAX_NOTES) { PrintMsg (GetSourcePos (LI), "Note", "%s", Msg); ++Output; } else { ++Skipped; } } } /* Add a note if we have more stuff that we won't output */ if (Skipped > 0) { const LineInfo* LI = CollConstAt (LineInfos, 0); PrintMsg (GetSourcePos (LI), "Note", "Dropping %u additional line infos", Skipped); } }
void WriteHLLDbgSyms (void) /* Write a list of all high level language symbols to the object file. */ { unsigned I; /* Only if debug info is enabled */ if (DbgSyms) { /* Write the symbol count to the list */ ObjWriteVar (CollCount (&HLLDbgSyms)); /* Walk through list and write all symbols to the file. */ for (I = 0; I < CollCount (&HLLDbgSyms); ++I) { /* Get the next symbol */ HLLDbgSym* S = CollAtUnchecked (&HLLDbgSyms, I); /* Get the type of the symbol */ unsigned SC = HLL_GET_SC (S->Flags); /* Remember if the symbol has debug info attached ** ### This should go into DbgInfoCheck */ if (S->Sym && S->Sym->DebugSymId != ~0U) { S->Flags |= HLL_DATA_SYM; } /* Write the symbol data */ ObjWriteVar (S->Flags); ObjWriteVar (S->Name); if (HLL_HAS_SYM (S->Flags)) { ObjWriteVar (S->Sym->DebugSymId); } if (SC == HLL_SC_AUTO || SC == HLL_SC_REG) { ObjWriteVar (S->Offs); } ObjWriteVar (S->Type); ObjWriteVar (S->Scope->Id); } } else { /* Write a count of zero */ ObjWriteVar (0); } }
static void ConvertImports (FILE* F, const O65Data* D) /* Convert the imports */ { unsigned I; if (CollCount (&D->Imports) > 0) { for (I = 0; I < CollCount (&D->Imports); ++I) { /* Get the next import */ const O65Import* Import = CollConstAt (&D->Imports, I); /* Import it by name */ fprintf (F, ".import\t%s\n", Import->Name); } fprintf (F, "\n"); } }
char* GetSearchPath (SearchPaths* P, unsigned Index) /* Return the search path at the given index, if the index is valid, return an ** empty string otherwise. */ { if (Index < CollCount (P)) return CollAtUnchecked (P, Index); return ""; }
struct DbgSym* GetObjDbgSym (const ObjData* O, unsigned Id) /* Get a debug symbol from an object file checking for a valid index */ { if (Id >= CollCount (&O->DbgSyms)) { Error ("Invalid debug symbol index (%u) in module `%s'", Id, GetObjFileName (O)); } return CollAtUnchecked (&O->DbgSyms, Id); }
struct Export* GetObjExport (const ObjData* O, unsigned Id) /* Get an export from an object file checking for a valid index */ { if (Id >= CollCount (&O->Exports)) { Error ("Invalid export index (%u) in module `%s'", Id, GetObjFileName (O)); } return CollAtUnchecked (&O->Exports, Id); }
struct Scope* GetObjScope (const ObjData* O, unsigned Id) /* Get a scope from an object file checking for a valid index */ { if (Id >= CollCount (&O->Scopes)) { Error ("Invalid scope index (%u) in module `%s'", Id, GetObjFileName (O)); } return CollAtUnchecked (&O->Scopes, Id); }
const char* GetCurrentFile (void) /* Return the name of the current input file */ { unsigned AFileCount = CollCount (&AFiles); if (AFileCount > 0) { const AFile* AF = (const AFile*) CollAt (&AFiles, AFileCount-1); return AF->Input->Name; } else { /* No open file. Use the main file if we have one. */ unsigned IFileCount = CollCount (&IFiles); if (IFileCount > 0) { const IFile* IF = (const IFile*) CollAt (&IFiles, 0); return IF->Name; } else { return "(outside file scope)"; } } }