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 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); } }
void CheckOpenIfs (void) /* Called from the scanner before closing an input file. Will check for any ** open .ifs in this file. */ { const LineInfo* LI; while (1) { /* Get the current file number and check if the topmost entry on the ** .IF stack was inserted with this file number */ IfDesc* D = GetCurrentIf (); if (D == 0) { /* There are no open .IFs */ break; } LI = CollConstAt (&D->LineInfos, 0); if (GetSourcePos (LI)->Name != CurTok.Pos.Name) { /* The .if is from another file, bail out */ break; } /* Start of .if is in the file we're about to leave */ LIError (&D->LineInfos, "Conditional assembly branch was never closed"); FreeIf (); } /* Calculate the new overall .IF condition */ CalcOverallIfCond (); }
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; } } } }
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; }
void DumpAttrColl (const Collection* C) /* Dump a collection of attribute/value pairs for debugging */ { unsigned I; for (I = 0; I < CollCount (C); ++I) { const Attr* A = CollConstAt (C, I); printf ("%s=%s\n", A->Name, A->Value); } }
void CheckAssertions (void) /* Check all assertions */ { unsigned I; /* Walk over all assertions */ for (I = 0; I < CollCount (&Assertions); ++I) { const LineInfo* LI; const char* Module; unsigned Line; /* Get the assertion */ Assertion* A = CollAtUnchecked (&Assertions, I); /* Ignore assertions that shouldn't be handled at link time */ if (!AssertAtLinkTime (A->Action)) { continue; } /* Retrieve the relevant line info for this assertion */ LI = CollConstAt (&A->LineInfos, 0); /* Get file name and line number from the source */ Module = GetSourceName (LI); Line = GetSourceLine (LI); /* If the expression is not constant, we're not able to handle it */ if (!IsConstExpr (A->Expr)) { Warning ("Cannot evaluate assertion in module `%s', line %u", Module, Line); } else if (GetExprVal (A->Expr) == 0) { /* Assertion failed */ const char* Message = GetString (A->Msg); switch (A->Action) { case ASSERT_ACT_WARN: case ASSERT_ACT_LDWARN: Warning ("%s(%u): %s", Module, Line, Message); break; case ASSERT_ACT_ERROR: case ASSERT_ACT_LDERROR: Error ("%s(%u): %s", Module, Line, Message); break; default: Internal ("Invalid assertion action (%u) in module `%s', " "line %u (file corrupt?)", A->Action, Module, Line); break; } } } }
static const char* GetString (const Collection* C, unsigned Index) /* Get a string from a collection. In fact, this function calls CollConstAt, * but will print a somewhat more readable error message if the index is out * of bounds. */ { if (Index >= CollCount (C)) { Error ("Invalid string index (%u) - file corrupt!", Index); } return CollConstAt (C, Index); }
const Attr* NeedAttr (const Collection* C, const char* Name, const char* Op) /* Search for an attribute with the given name and return it. If the attribute * is not found, the function terminates with an error using Op as additional * context in the error message. */ { /* Search for the attribute and return it */ unsigned Index; if (!FindAttr (C, Name, &Index)) { Error ("Found no attribute named `%s' for operation %s", Name, Op); } return CollConstAt (C, Index); }
const StrBuf* GetFileName (unsigned Name) /* Get the name of a file where the name index is known */ { static const StrBuf ErrorMsg = LIT_STRBUF_INITIALIZER ("(outside file scope)"); const FileEntry* F; if (Name == 0) { /* Name was defined outside any file scope, use the name of the first * file instead. Errors are then reported with a file position of * line zero in the first file. */ if (CollCount (&FileTab) == 0) { /* No files defined until now */ return &ErrorMsg; } else { F = CollConstAt (&FileTab, 0); } } else { F = CollConstAt (&FileTab, Name-1); } return GetStrBuf (F->Name); }
const Attr* GetAttr (const Collection* C, const char* Name) /* Search for an attribute with the given name and return it. The function * returns NULL if the attribute wasn't found. */ { /* Search for the attribute and return it */ unsigned Index; if (FindAttr (C, Name, &Index)) { return CollConstAt (C, Index); } else { /* Not found */ return 0; } }
void ErrorMsg (const Collection* LineInfos, const char* Format, va_list ap) /* Print an error message */ { /* The first entry in the collection is that of the actual source pos */ const LineInfo* LI = CollConstAt (LineInfos, 0); /* Output an error for this position */ VPrintMsg (GetSourcePos (LI), "Error", Format, ap); /* Add additional notifications if necessary */ AddNotifications (LineInfos); /* Count errors */ ++ErrorCount; }
static void WarningMsg (const Collection* LineInfos, const char* Format, va_list ap) /* Print warning message. */ { /* The first entry in the collection is that of the actual source pos */ const LineInfo* LI = CollConstAt (LineInfos, 0); /* Output a warning for this position */ VPrintMsg (GetSourcePos (LI), "Warning", Format, ap); /* Add additional notifications if necessary */ AddNotifications (LineInfos); /* Count warnings */ ++WarningCount; }
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"); } }
static const char* RelocExpr (const O65Data* D, unsigned char SegID, unsigned long Val, const O65Reloc* R) /* Generate the segment relative relocation expression. R is only used if the * expression contains am import, and may be NULL if this is an error (which * is then flagged). */ { const O65Import* Import; switch (SegID) { case O65_SEGID_UNDEF: if (R) { if (R->SymIdx >= CollCount (&D->Imports)) { Error ("Import index out of range (input file corrupt)"); } Import = CollConstAt (&D->Imports, R->SymIdx); return LabelPlusOffs (Import->Name, Val); } else { Error ("Relocation references an import which is not allowed here"); return 0; } break; case O65_SEGID_TEXT: return LabelPlusOffs (CodeLabel, Val - D->Header.tbase); case O65_SEGID_DATA: return LabelPlusOffs (DataLabel, Val - D->Header.dbase); case O65_SEGID_BSS: return LabelPlusOffs (BssLabel, Val - D->Header.bbase); case O65_SEGID_ZP: return LabelPlusOffs (ZeropageLabel, Val - D->Header.zbase); case O65_SEGID_ABS: return LabelPlusOffs ("", Val); default: Internal ("Cannot handle this segment reference in reloc entry"); } /* NOTREACHED */ return 0; }
static void WriteIndex (void) /* Write the index of a library file */ { unsigned I; /* Sync I/O in case the last operation was a read */ fseek (NewLib, 0, SEEK_CUR); /* Remember the current offset in the header */ Header.IndexOffs = ftell (NewLib); /* Write the object file count */ WriteVar (NewLib, CollCount (&ObjPool)); /* Write the object files */ for (I = 0; I < CollCount (&ObjPool); ++I) { WriteIndexEntry (CollConstAt (&ObjPool, I)); } }
int PushSearchPath (SearchPaths* P, const char* NewPath) /* Add a new search path to the head of an existing search path list, provided ** that it's not already there. If the path is already at the first position, ** return zero, otherwise return a non zero value. */ { /* Generate a clean copy of NewPath */ char* Path = CleanupPath (NewPath); /* If we have paths, check if Path is already at position zero */ if (CollCount (P) > 0 && strcmp (CollConstAt (P, 0), Path) == 0) { /* Match. Delete the copy and return to the caller */ xfree (Path); return 0; } /* Insert a clean copy of the path at position 0, return success */ CollInsert (P, Path, 0); return 1; }
static void LibCheckExports (ObjData* O) /* Insert all exports from the given object file into the global list * checking for duplicates. */ { unsigned I; /* Let the user know what we do */ Print (stdout, 1, "Module `%s' (%u exports):\n", O->Name, CollCount (&O->Exports)); /* Insert the exports into the global table */ for (I = 0; I < CollCount (&O->Exports); ++I) { /* Get the name of the export */ const char* Name = CollConstAt (&O->Exports, I); /* Insert the name into the hash table */ Print (stdout, 1, " %s\n", Name); ExpInsert (Name, O); } }
static void ConvertExports (FILE* F, const O65Data* D) /* Convert the exports */ { unsigned I; if (CollCount (&D->Exports) > 0) { for (I = 0; I < CollCount (&D->Exports); ++I) { /* Get the next import */ const O65Export* Export = CollConstAt (&D->Exports, I); /* First define it */ fprintf (F, "%s = %s\n", Export->Name, RelocExpr (D, Export->SegID, Export->Val, 0)); /* Then export it by name */ fprintf (F, ".export\t%s\n", Export->Name); } fprintf (F, "\n"); } }
void WriteFiles (void) /* Write the list of input files to the object file */ { unsigned I; /* Tell the obj file module that we're about to start the file list */ ObjStartFiles (); /* Write the file count */ ObjWriteVar (CollCount (&FileTab)); /* Write the file data */ for (I = 0; I < CollCount (&FileTab); ++I) { /* Get a pointer to the entry */ const FileEntry* F = CollConstAt (&FileTab, I); /* Write the fields */ ObjWriteVar (F->Name); ObjWrite32 (F->MTime); ObjWriteVar (F->Size); } /* Done writing files */ ObjEndFiles (); }
static void ConvertSeg (FILE* F, const O65Data* D, const Collection* Relocs, const unsigned char* Data, unsigned long Size) /* Convert one segment */ { const O65Reloc* R; unsigned RIdx; unsigned long Byte; /* Get the pointer to the first relocation entry if there are any */ R = (CollCount (Relocs) > 0)? CollConstAt (Relocs, 0) : 0; /* Initialize for the loop */ RIdx = 0; Byte = 0; /* Walk over the segment data */ while (Byte < Size) { if (R && R->Offs == Byte) { /* We've reached an entry that must be relocated */ unsigned long Val; switch (R->Type) { case O65_RTYPE_WORD: if (Byte >= Size - 1) { Error ("Found WORD relocation, but not enough bytes left"); } else { Val = (Data[Byte+1] << 8) + Data[Byte]; Byte += 2; fprintf (F, "\t.word\t%s\n", RelocExpr (D, R->SegID, Val, R)); } break; case O65_RTYPE_HIGH: Val = (Data[Byte++] << 8) + R->Val; fprintf (F, "\t.byte\t>(%s)\n", RelocExpr (D, R->SegID, Val, R)); break; case O65_RTYPE_LOW: Val = Data[Byte++]; fprintf (F, "\t.byte\t<(%s)\n", RelocExpr (D, R->SegID, Val, R)); break; case O65_RTYPE_SEGADDR: if (Byte >= Size - 2) { Error ("Found SEGADDR relocation, but not enough bytes left"); } else { Val = (((unsigned long) Data[Byte+2]) << 16) + (((unsigned long) Data[Byte+1]) << 8) + (((unsigned long) Data[Byte+0]) << 0) + R->Val; Byte += 3; fprintf (F, "\t.faraddr\t%s\n", RelocExpr (D, R->SegID, Val, R)); } break; case O65_RTYPE_SEG: /* FALLTHROUGH for now */ default: Internal ("Cannot handle relocation type %d at %lu", R->Type, Byte); } /* Get the next relocation entry */ if (++RIdx < CollCount (Relocs)) { R = CollConstAt (Relocs, RIdx); } else { R = 0; } } else { /* Just a constant value */ fprintf (F, "\t.byte\t$%02X\n", Data[Byte++]); } } fprintf (F, "\n"); }
void Convert (const O65Data* D) /* Convert the o65 file in D using the given output file. */ { FILE* F; unsigned I; char* Author = 0; /* For now, we do only accept o65 files generated by the ld65 linker which * have a specific format. */ if (!Debug && D->Header.mode != O65_MODE_CC65) { Error ("Cannot convert o65 files of this type"); } /* Output statistics */ PrintO65Stats (D); /* Walk through the options and print them if verbose mode is enabled. * Check for a os=cc65 option and bail out if we didn't find one (for * now - later we switch to special handling). */ for (I = 0; I < CollCount (&D->Options); ++I) { /* Get the next option */ const O65Option* O = CollConstAt (&D->Options, I); /* Check the type of the option */ switch (O->Type) { case O65_OPT_FILENAME: Print (stdout, 1, "O65 filename option: `%s'\n", GetO65OptionText (O)); break; case O65_OPT_OS: if (O->Len == 2) { Warning ("Operating system option without data found"); } else { Print (stdout, 1, "O65 operating system option: `%s'\n", GetO65OSName (O->Data[0])); switch (O->Data[0]) { case O65_OS_CC65_MODULE: if (Model != O65_MODEL_NONE && Model != O65_MODEL_CC65_MODULE) { Warning ("Wrong o65 model for input file specified"); } else { Model = O65_MODEL_CC65_MODULE; } break; } } break; case O65_OPT_ASM: Print (stdout, 1, "O65 assembler option: `%s'\n", GetO65OptionText (O)); break; case O65_OPT_AUTHOR: if (Author) { xfree (Author); } Author = xstrdup (GetO65OptionText (O)); Print (stdout, 1, "O65 author option: `%s'\n", Author); break; case O65_OPT_TIMESTAMP: Print (stdout, 1, "O65 timestamp option: `%s'\n", GetO65OptionText (O)); break; default: Warning ("Found unknown option, type %d, length %d", O->Type, O->Len); break; } } /* If we shouldn't generate output, we're done here */ if (NoOutput) { return; } /* Open the output file */ F = fopen (OutputName, "w"); if (F == 0) { Error ("Cannot open `%s': %s", OutputName, strerror (errno)); } /* Create a header */ fprintf (F, ";\n; File generated by co65 v %s using model `%s'\n;\n", GetVersionAsString (), GetModelName (Model)); /* Select the CPU */ if ((D->Header.mode & O65_CPU_MASK) == O65_CPU_65816) { fprintf (F, ".p816\n"); } /* Object file options */ fprintf (F, ".fopt\t\tcompiler,\"co65 v %s\"\n", GetVersionAsString ()); if (Author) { fprintf (F, ".fopt\t\tauthor, \"%s\"\n", Author); xfree (Author); Author = 0; } /* Several other assembler options */ fprintf (F, ".case\t\ton\n"); fprintf (F, ".debuginfo\t%s\n", (DebugInfo != 0)? "on" : "off"); /* Setup/export the segment labels */ SetupSegLabels (F); /* End of header */ fprintf (F, "\n"); /* Imported identifiers */ ConvertImports (F, D); /* Exported identifiers */ ConvertExports (F, D); /* Code segment */ ConvertCodeSeg (F, D); /* Data segment */ ConvertDataSeg (F, D); /* BSS segment */ ConvertBssSeg (F, D); /* Zero page segment */ ConvertZeropageSeg (F, D); /* End of data */ fprintf (F, ".end\n"); fclose (F); }
void CS_Output (CodeSeg* S) /* Output the code segment data to the output file */ { unsigned I; const LineInfo* LI; /* Get the number of entries in this segment */ unsigned Count = CS_GetEntryCount (S); /* If the code segment is empty, bail out here */ if (Count == 0) { return; } /* Generate register info */ CS_GenRegInfo (S); /* Output the segment directive */ WriteOutput (".segment\t\"%s\"\n\n", S->SegName); /* Output all entries, prepended by the line information if it has changed */ LI = 0; for (I = 0; I < Count; ++I) { /* Get the next entry */ const CodeEntry* E = CollConstAt (&S->Entries, I); /* Check if the line info has changed. If so, output the source line * if the option is enabled and output debug line info if the debug * option is enabled. */ if (E->LI != LI) { /* Line info has changed, remember the new line info */ LI = E->LI; /* Add the source line as a comment. Beware: When line continuation * was used, the line may contain newlines. */ if (AddSource) { const char* L = LI->Line; WriteOutput (";\n; "); while (*L) { const char* N = strchr (L, '\n'); if (N) { /* We have a newline, just write the first part */ WriteOutput ("%.*s\n; ", (int) (N - L), L); L = N+1; } else { /* No Newline, write as is */ WriteOutput ("%s\n", L); break; } } WriteOutput (";\n"); } /* Add line debug info */ if (DebugInfo) { WriteOutput ("\t.dbg\tline, \"%s\", %u\n", GetInputName (LI), GetInputLine (LI)); } } /* Output the code */ CE_Output (E); } /* If debug info is enabled, terminate the last line number information */ if (DebugInfo) { WriteOutput ("\t.dbg\tline\n"); } /* Free register info */ CS_FreeRegInfo (S); }