static void CreateRunDefines (SegDesc* S, unsigned long SegAddr) /* Create the defines for a RUN segment */ { StrBuf Buf = STATIC_STRBUF_INITIALIZER; SB_Printf (&Buf, "__%s_RUN__", GetString (S->Name)); CreateMemoryExport (GetStrBufId (&Buf), S->Run, SegAddr - S->Run->Start); SB_Printf (&Buf, "__%s_SIZE__", GetString (S->Name)); CreateConstExport (GetStrBufId (&Buf), S->Seg->Size); S->Flags |= SF_RUN_DEF; SB_Done (&Buf); }
static void VPrintMsg (const FilePos* Pos, const char* Desc, const char* Format, va_list ap) /* Format and output an error/warning message. */ { StrBuf S = STATIC_STRBUF_INITIALIZER; /* Format the actual message */ StrBuf Msg = STATIC_STRBUF_INITIALIZER; SB_VPrintf (&Msg, Format, ap); SB_Terminate (&Msg); /* Format the message header */ SB_Printf (&S, "%s(%u): %s: ", SB_GetConstBuf (GetFileName (Pos->Name)), Pos->Line, Desc); /* Append the message to the message header */ SB_Append (&S, &Msg); /* Delete the formatted message */ SB_Done (&Msg); /* Add a new line and terminate the generated full message */ SB_AppendChar (&S, '\n'); SB_Terminate (&S); /* Output the full message */ fputs (SB_GetConstBuf (&S), stderr); /* Delete the buffer for the full message */ SB_Done (&S); }
StrBuf* AnonName (StrBuf* Buf, const char* Spec) /* Get a name for an anonymous scope, variable or type. Size is the size of * the buffer passed to the function, Spec will be used as part of the * identifier if given. A pointer to the buffer is returned. */ { static unsigned ACount = 0; SB_Printf (Buf, "%s-%s-%04X", AnonTag, Spec, ++ACount); return Buf; }
static void CreateLoadDefines (SegDesc* S, unsigned long SegAddr) /* Create the defines for a LOAD segment */ { StrBuf Buf = STATIC_STRBUF_INITIALIZER; SB_Printf (&Buf, "__%s_LOAD__", GetString (S->Name)); CreateMemoryExport (GetStrBufId (&Buf), S->Load, SegAddr - S->Load->Start); S->Flags |= SF_LOAD_DEF; SB_Done (&Buf); }
static void FuncString (void) /* Handle the .STRING function */ { StrBuf Buf = STATIC_STRBUF_INITIALIZER; /* Skip it */ NextTok (); /* Left paren expected */ ConsumeLParen (); /* Accept identifiers or numeric expressions */ if (CurTok.Tok == TOK_LOCAL_IDENT) { /* Save the identifier, then skip it */ SB_Copy (&Buf, &CurTok.SVal); NextTok (); } else if (CurTok.Tok == TOK_NAMESPACE || CurTok.Tok == TOK_IDENT) { /* Parse a fully qualified symbol name. We cannot use * ParseScopedSymName here since the name may be invalid. */ int NameSpace; do { NameSpace = (CurTok.Tok == TOK_NAMESPACE); if (NameSpace) { SB_AppendStr (&Buf, "::"); } else { SB_Append (&Buf, &CurTok.SVal); } NextTok (); } while ((NameSpace != 0 && CurTok.Tok == TOK_IDENT) || (NameSpace == 0 && CurTok.Tok == TOK_NAMESPACE)); } else { /* Numeric expression */ long Val = ConstExpression (); SB_Printf (&Buf, "%ld", Val); } /* We expect a closing parenthesis, but will not skip it but replace it * by the string token just created. */ if (CurTok.Tok != TOK_RPAREN) { Error ("`)' expected"); } else { CurTok.Tok = TOK_STRCON; SB_Copy (&CurTok.SVal, &Buf); SB_Terminate (&CurTok.SVal); } /* Free string memory */ SB_Done (&Buf); }
static void SetOptions (void) /* Set the option for the translator */ { StrBuf Buf = STATIC_STRBUF_INITIALIZER; /* Set the translator */ SB_Printf (&Buf, "ca65 V%s", GetVersionAsString ()); OptTranslator (&Buf); /* Set date and time */ OptDateTime ((unsigned long) time(0)); /* Release memory for the string */ SB_Done (&Buf); }
const char* ED_GetLabelName (const ExprDesc* Expr, long Offs) /* Return the assembler label name of the given expression. Beware: This * function may use a static buffer, so the name may get "lost" on the second * call to the function. */ { static StrBuf Buf = STATIC_STRBUF_INITIALIZER; /* Expr may have it's own offset, adjust Offs accordingly */ Offs += Expr->IVal; /* Generate a label depending on the location */ switch (ED_GetLoc (Expr)) { case E_LOC_ABS: /* Absolute: numeric address or const */ SB_Printf (&Buf, "$%04X", (int)(Offs & 0xFFFF)); break; case E_LOC_GLOBAL: case E_LOC_STATIC: /* Global or static variable */ if (Offs) { SB_Printf (&Buf, "%s%+ld", SymGetAsmName (Expr->Sym), Offs); } else { SB_Printf (&Buf, "%s", SymGetAsmName (Expr->Sym)); } break; case E_LOC_REGISTER: /* Register variable */ SB_Printf (&Buf, "regbank+%u", (unsigned)((Offs + Expr->Name) & 0xFFFFU)); break; case E_LOC_LITERAL: /* Literal in the literal pool */ if (Offs) { SB_Printf (&Buf, "%s%+ld", LocalLabelName (Expr->Name), Offs); } else { SB_Printf (&Buf, "%s", LocalLabelName (Expr->Name)); } break; default: Internal ("Invalid location in ED_GetLabelName: 0x%04X", ED_GetLoc (Expr)); } /* Return a pointer to the static buffer */ return SB_GetConstBuf (&Buf); }
static void FuncSPrintF (void) /* Handle the .SPRINTF function */ { StrBuf Format = STATIC_STRBUF_INITIALIZER; /* User supplied format */ StrBuf R = STATIC_STRBUF_INITIALIZER; /* Result string */ StrBuf F1 = STATIC_STRBUF_INITIALIZER; /* One format spec from F */ StrBuf R1 = STATIC_STRBUF_INITIALIZER; /* One result */ char C; int Done; long IVal; /* Integer value */ /* Skip the .SPRINTF token */ NextTok (); /* Left paren expected */ ConsumeLParen (); /* First argument is a format string. Remember and skip it */ if (!LookAtStrCon ()) { return; } SB_Copy (&Format, &CurTok.SVal); NextTok (); /* Walk over the format string, generating the function result in R */ while (1) { /* Get the next char from the format string and check for EOS */ if (SB_Peek (&Format) == '\0') { break; } /* Check for a format specifier */ if (SB_Peek (&Format) != '%') { /* No format specifier, just copy */ SB_AppendChar (&R, SB_Get (&Format)); continue; } SB_Skip (&Format); if (SB_Peek (&Format) == '%') { /* %% */ SB_AppendChar (&R, '%'); SB_Skip (&Format); continue; } if (SB_Peek (&Format) == '\0') { InvalidFormatString (); break; } /* Since a format specifier follows, we do expect anotehr argument for * the .sprintf function. */ ConsumeComma (); /* We will copy the format spec into F1 checking for the things we * support, and later use xsprintf to do the actual formatting. This * is easier than adding another printf implementation... */ SB_Clear (&F1); SB_AppendChar (&F1, '%'); /* Check for flags */ Done = 0; while ((C = SB_Peek (&Format)) != '\0' && !Done) { switch (C) { case '-': /* FALLTHROUGH */ case '+': /* FALLTHROUGH */ case ' ': /* FALLTHROUGH */ case '#': /* FALLTHROUGH */ case '0': SB_AppendChar (&F1, SB_Get (&Format)); break; default: Done = 1; break; } } /* We do only support a numerical width field */ while (IsDigit (SB_Peek (&Format))) { SB_AppendChar (&F1, SB_Get (&Format)); } /* Precision - only positive numerical fields supported */ if (SB_Peek (&Format) == '.') { SB_AppendChar (&F1, SB_Get (&Format)); while (IsDigit (SB_Peek (&Format))) { SB_AppendChar (&F1, SB_Get (&Format)); } } /* Length modifiers aren't supported, so read the conversion specs */ switch (SB_Peek (&Format)) { case 'd': case 'i': case 'o': case 'u': case 'X': case 'x': /* Our ints are actually longs, so we use the 'l' modifier when * calling xsprintf later. Terminate the format string. */ SB_AppendChar (&F1, 'l'); SB_AppendChar (&F1, SB_Get (&Format)); SB_Terminate (&F1); /* The argument must be a constant expression */ IVal = ConstExpression (); /* Format this argument according to the spec */ SB_Printf (&R1, SB_GetConstBuf (&F1), IVal); /* Append the formatted argument to the result */ SB_Append (&R, &R1); break; case 's': /* Add the format spec and terminate the format */ SB_AppendChar (&F1, SB_Get (&Format)); SB_Terminate (&F1); /* The argument must be a string constant */ if (!LookAtStrCon ()) { /* Make it one */ SB_CopyStr (&CurTok.SVal, "**undefined**"); } /* Format this argument according to the spec */ SB_Printf (&R1, SB_GetConstBuf (&F1), SB_GetConstBuf (&CurTok.SVal)); /* Skip the string constant */ NextTok (); /* Append the formatted argument to the result */ SB_Append (&R, &R1); break; case 'c': /* Add the format spec and terminate the format */ SB_AppendChar (&F1, SB_Get (&Format)); SB_Terminate (&F1); /* The argument must be a constant expression */ IVal = ConstExpression (); /* Check for a valid character range */ if (IVal <= 0 || IVal > 255) { Error ("Char argument out of range"); IVal = ' '; } /* Format this argument according to the spec. Be sure to pass * an int as the char value. */ SB_Printf (&R1, SB_GetConstBuf (&F1), (int) IVal); /* Append the formatted argument to the result */ SB_Append (&R, &R1); break; default: Error ("Invalid format string"); SB_Skip (&Format); break; } } /* Terminate the result string */ SB_Terminate (&R); /* We expect a closing parenthesis, but will not skip it but replace it * by the string token just created. */ if (CurTok.Tok != TOK_RPAREN) { Error ("`)' expected"); } else { CurTok.Tok = TOK_STRCON; SB_Copy (&CurTok.SVal, &R); SB_Terminate (&CurTok.SVal); } /* Delete the string buffers */ SB_Done (&Format); SB_Done (&R); SB_Done (&F1); SB_Done (&R1); }
static void CheckSymType (const Export* E) /* Check the types for one export */ { /* External with matching imports */ Import* I = E->ImpList; while (I) { if (E->AddrSize != I->AddrSize) { /* Export and import address sizes do not match */ StrBuf ExportLoc = STATIC_STRBUF_INITIALIZER; StrBuf ImportLoc = STATIC_STRBUF_INITIALIZER; const char* ExpAddrSize = AddrSizeToStr ((unsigned char) E->AddrSize); const char* ImpAddrSize = AddrSizeToStr ((unsigned char) I->AddrSize); const LineInfo* ExportLI = GetExportPos (E); const LineInfo* ImportLI = GetImportPos (I); /* Generate strings that describe the location of the im- and ** exports. This depends on the place from where they come: ** Object file or linker config. */ if (E->Obj) { /* The export comes from an object file */ SB_Printf (&ExportLoc, "%s, %s(%u)", GetString (E->Obj->Name), GetSourceName (ExportLI), GetSourceLine (ExportLI)); } else { SB_Printf (&ExportLoc, "%s(%u)", GetSourceName (ExportLI), GetSourceLine (ExportLI)); } if (I->Obj) { /* The import comes from an object file */ SB_Printf (&ImportLoc, "%s, %s(%u)", GetString (I->Obj->Name), GetSourceName (ImportLI), GetSourceLine (ImportLI)); } else if (ImportLI) { /* The import is linker generated and we have line ** information */ SB_Printf (&ImportLoc, "%s(%u)", GetSourceName (ImportLI), GetSourceLine (ImportLI)); } else { /* The import is linker generated and we don't have line ** information */ SB_Printf (&ImportLoc, "%s", GetObjFileName (I->Obj)); } /* Output the diagnostic */ Warning ("Address size mismatch for `%s': " "Exported from %s as `%s', " "import in %s as `%s'", GetString (E->Name), SB_GetConstBuf (&ExportLoc), ExpAddrSize, SB_GetConstBuf (&ImportLoc), ImpAddrSize); /* Free the temporary strings */ SB_Done (&ExportLoc); SB_Done (&ImportLoc); } I = I->Next; } }
static int MacExpand (void* Data) /* If we're currently expanding a macro, set the the scanner token and * attribute to the next value and return true. If we are not expanding * a macro, return false. */ { /* Cast the Data pointer to the actual data structure */ MacExp* Mac = (MacExp*) Data; /* Check if we should abort this macro */ if (DoMacAbort) { /* Reset the flag */ DoMacAbort = 0; /* Abort any open .IF statements in this macro expansion */ CleanupIfStack (Mac->IfSP); /* Terminate macro expansion */ goto MacEnd; } /* We're expanding a macro. Check if we are expanding one of the * macro parameters. */ ExpandParam: if (Mac->ParamExp) { /* Ok, use token from parameter list */ TokSet (Mac->ParamExp); /* Create new line info for this parameter token */ if (Mac->ParamLI) { EndLine (Mac->ParamLI); } Mac->ParamLI = StartLine (&CurTok.Pos, LI_TYPE_MACPARAM, Mac->MacExpansions); /* Set pointer to next token */ Mac->ParamExp = Mac->ParamExp->Next; /* Done */ return 1; } else if (Mac->ParamLI) { /* There's still line info open from the parameter expansion - end it */ EndLine (Mac->ParamLI); Mac->ParamLI = 0; } /* We're not expanding macro parameters. Check if we have tokens left from * the macro itself. */ if (Mac->Exp) { /* Use next macro token */ TokSet (Mac->Exp); /* Create new line info for this token */ if (Mac->LI) { EndLine (Mac->LI); } Mac->LI = StartLine (&CurTok.Pos, LI_TYPE_MACRO, Mac->MacExpansions); /* Set pointer to next token */ Mac->Exp = Mac->Exp->Next; /* Is it a request for actual parameter count? */ if (CurTok.Tok == TOK_PARAMCOUNT) { CurTok.Tok = TOK_INTCON; CurTok.IVal = Mac->ParamCount; return 1; } /* Is it the name of a macro parameter? */ if (CurTok.Tok == TOK_MACPARAM) { /* Start to expand the parameter token list */ Mac->ParamExp = Mac->Params[CurTok.IVal]; /* Go back and expand the parameter */ goto ExpandParam; } /* If it's an identifier, it may in fact be a local symbol */ if ((CurTok.Tok == TOK_IDENT || CurTok.Tok == TOK_LOCAL_IDENT) && Mac->M->LocalCount) { /* Search for the local symbol in the list */ unsigned Index = 0; IdDesc* I = Mac->M->Locals; while (I) { if (SB_Compare (&CurTok.SVal, &I->Id) == 0) { /* This is in fact a local symbol, change the name. Be sure * to generate a local label name if the original name was * a local label, and also generate a name that cannot be * generated by a user. */ if (SB_At (&I->Id, 0) == LocalStart) { /* Must generate a local symbol */ SB_Printf (&CurTok.SVal, "%cLOCAL-MACRO_SYMBOL-%04X", LocalStart, Mac->LocalStart + Index); } else { /* Global symbol */ SB_Printf (&CurTok.SVal, "LOCAL-MACRO_SYMBOL-%04X", Mac->LocalStart + Index); } break; } /* Next symbol */ ++Index; I = I->Next; } /* Done */ return 1; } /* The token was successfully set */ return 1; } /* No more macro tokens. Do we have a final token? */ if (Mac->Final) { /* Set the final token and remove it */ TokSet (Mac->Final); FreeTokNode (Mac->Final); Mac->Final = 0; /* Problem: When a .define style macro is expanded within the call * of a classic one, the latter may be terminated and removed while * the expansion of the .define style macro is still active. Because * line info slots are "stacked", this runs into a CHECK FAILED. For * now, we will fix that by removing the .define style macro expansion * immediately, once the final token is placed. The better solution * would probably be to not require AllocLineInfoSlot/FreeLineInfoSlot * to be called in FIFO order, but this is a bigger change. */ /* End of macro expansion and pop the input function */ FreeMacExp (Mac); PopInput (); /* The token was successfully set */ return 1; } MacEnd: /* End of macro expansion */ FreeMacExp (Mac); /* Pop the input function */ PopInput (); /* No token available */ return 0; }
static int MacExpand (void* Data) /* If we're currently expanding a macro, set the the scanner token and * attribute to the next value and return true. If we are not expanding * a macro, return false. */ { /* Cast the Data pointer to the actual data structure */ MacExp* Mac = (MacExp*) Data; /* Check if we should abort this macro */ if (DoMacAbort) { /* Reset the flag */ DoMacAbort = 0; /* Abort any open .IF statements in this macro expansion */ CleanupIfStack (Mac->IfSP); /* Terminate macro expansion */ goto MacEnd; } /* We're expanding a macro. Check if we are expanding one of the * macro parameters. */ if (Mac->ParamExp) { /* Ok, use token from parameter list */ TokSet (Mac->ParamExp); /* Set pointer to next token */ Mac->ParamExp = Mac->ParamExp->Next; /* Done */ return 1; } /* We're not expanding macro parameters. Check if we have tokens left from * the macro itself. */ if (Mac->Exp) { /* Use next macro token */ TokSet (Mac->Exp); /* Set pointer to next token */ Mac->Exp = Mac->Exp->Next; /* Is it a request for actual parameter count? */ if (Tok == TOK_PARAMCOUNT) { Tok = TOK_INTCON; IVal = Mac->ParamCount; return 1; } /* Is it the name of a macro parameter? */ if (Tok == TOK_MACPARAM) { /* Start to expand the parameter token list */ Mac->ParamExp = Mac->Params [IVal]; /* Recursive call to expand the parameter */ return MacExpand (Mac); } /* If it's an identifier, it may in fact be a local symbol */ if ((Tok == TOK_IDENT || Tok == TOK_LOCAL_IDENT) && Mac->M->LocalCount) { /* Search for the local symbol in the list */ unsigned Index = 0; IdDesc* I = Mac->M->Locals; while (I) { if (SB_Compare (&SVal, &I->Id) == 0) { /* This is in fact a local symbol, change the name. Be sure * to generate a local label name if the original name was * a local label, and also generate a name that cannot be * generated by a user. */ if (SB_At (&I->Id, 0) == LocalStart) { /* Must generate a local symbol */ SB_Printf (&SVal, "%cLOCAL-MACRO_SYMBOL-%04X", LocalStart, Mac->LocalStart + Index); } else { /* Global symbol */ SB_Printf (&SVal, "LOCAL-MACRO_SYMBOL-%04X", Mac->LocalStart + Index); } break; } /* Next symbol */ ++Index; I = I->Next; } /* Done */ return 1; } /* The token was successfully set */ return 1; } /* No more macro tokens. Do we have a final token? */ if (Mac->Final) { /* Set the final token and remove it */ TokSet (Mac->Final); FreeTokNode (Mac->Final); Mac->Final = 0; /* The token was successfully set */ return 1; } MacEnd: /* End of macro expansion */ FreeMacExp (Mac); /* Pop the input function */ PopInput (); /* No token available */ return 0; }
unsigned CfgAssignSegments (void) /* Assign segments, define linker symbols where requested. The function will * return the number of memory area overflows (so zero means anything went ok). * In case of overflows, a short mapfile can be generated later, to ease the * task of rearranging segments for the user. */ { unsigned Overflows = 0; /* Walk through each of the memory sections. Add up the sizes and check * for an overflow of the section. Assign the start addresses of the * segments while doing this. */ Memory* M = MemoryList; while (M) { MemListNode* N; /* Get the start address of this memory area */ unsigned long Addr = M->Start; /* Remember if this is a relocatable memory area */ M->Relocatable = RelocatableBinFmt (M->F->Format); /* Walk through the segments in this memory area */ N = M->SegList; while (N) { /* Get the segment from the node */ SegDesc* S = N->Seg; /* Some actions depend on wether this is the load or run memory * area. */ if (S->Run == M) { /* This is the run (and maybe load) memory area. Handle * alignment and explict start address and offset. */ if (S->Flags & SF_ALIGN) { /* Align the address */ unsigned long Val = (0x01UL << S->Align) - 1; Addr = (Addr + Val) & ~Val; } else if (S->Flags & (SF_OFFSET | SF_START)) { /* Give the segment a fixed starting address */ unsigned long NewAddr = S->Addr; if (S->Flags & SF_OFFSET) { /* An offset was given, no address, make an address */ NewAddr += M->Start; } if (Addr > NewAddr) { /* Offset already too large */ if (S->Flags & SF_OFFSET) { Error ("Offset too small in `%s', segment `%s'", GetString (M->Name), GetString (S->Name)); } else { Error ("Start address too low in `%s', segment `%s'", GetString (M->Name), GetString (S->Name)); } } Addr = NewAddr; } /* Set the start address of this segment, set the readonly flag * in the segment and and remember if the segment is in a * relocatable file or not. */ S->Seg->PC = Addr; S->Seg->ReadOnly = (S->Flags & SF_RO) != 0; S->Seg->Relocatable = M->Relocatable; } else if (S->Load == M) { /* This is the load memory area, *and* run and load are * different (because of the "else" above). Handle alignment. */ if (S->Flags & SF_ALIGN_LOAD) { /* Align the address */ unsigned long Val = (0x01UL << S->AlignLoad) - 1; Addr = (Addr + Val) & ~Val; } } /* Increment the fill level of the memory area and check for an * overflow. */ M->FillLevel = Addr + S->Seg->Size - M->Start; if (M->FillLevel > M->Size && (M->Flags & MF_OVERFLOW) == 0) { ++Overflows; M->Flags |= MF_OVERFLOW; Warning ("Memory area overflow in `%s', segment `%s' (%lu bytes)", GetString (M->Name), GetString (S->Name), M->FillLevel - M->Size); } /* If requested, define symbols for the start and size of the * segment. */ if (S->Flags & SF_DEFINE) { if (S->Run == M && (S->Flags & SF_RUN_DEF) == 0) { CreateRunDefines (S, Addr); } if (S->Load == M && (S->Flags & SF_LOAD_DEF) == 0) { CreateLoadDefines (S, Addr); } } /* Calculate the new address */ Addr += S->Seg->Size; /* Next segment */ N = N->Next; } /* If requested, define symbols for start and size of the memory area */ if (M->Flags & MF_DEFINE) { StrBuf Buf = STATIC_STRBUF_INITIALIZER; SB_Printf (&Buf, "__%s_START__", GetString (M->Name)); CreateMemoryExport (GetStrBufId (&Buf), M, 0); SB_Printf (&Buf, "__%s_SIZE__", GetString (M->Name)); CreateConstExport (GetStrBufId (&Buf), M->Size); SB_Printf (&Buf, "__%s_LAST__", GetString (M->Name)); CreateMemoryExport (GetStrBufId (&Buf), M, M->FillLevel); SB_Done (&Buf); } /* Next memory area */ M = M->Next; } /* Return the number of memory area overflows */ return Overflows; }