better way is to declare characters explicitly as "signed" if needed. You
can also use <tt><ref id="pragma-signed-chars"
name="#pragma signed-chars"></tt> for better control of this option.
-
+
<label id="option--standard">
<tag><tt>--standard std</tt></tag>
<tag><tt>--writable-strings</tt></tag>
Make string literals writable by placing them into the data segment instead
- of the rodata segment.
+ of the rodata segment. You can also use <tt><ref id="pragma-writable-strings"
+ name="#pragma writable-strings"></tt> to control this option on a
+ per function basis.
<label id="option-static-locals">
#pragma warn (unused-param, pop)
</verb></tscreen>
+<sect1><tt>#pragma writable-strings ([push,] on|off)</tt><label id="pragma-writable-strings"><p>
+
+ Changes the storage location of string literals. For historical reasons,
+ the C standard defines that string literals are of type "char[]", but
+ writing to such a literal causes undefined behaviour. Most compilers
+ (including cc65) place string literals in the read-only data segment, which
+ may cause problems with old C code that writes to string literals.
+
+ Using this pragma (or the corresponding command line option <tt/<ref
+ name="--writable-strings" id="option-writable-strings">/) causes the
+ literals to be placed in the data segment so they can be written to without
+ worry.
+
+ Please note that the value of this flag that is in effect when a function
+ is encountered, determines where the literals are stored. Changing the
+ <tt/#pragma/ within a function doesn't have an effect for this function.
+
+ The <tt/#pragma/ understands the push and pop parameters as explained above.
+
+
<sect1><tt>#pragma zpsym (<name>)</tt><p>
Tell the compiler that the -- previously as external declared -- symbol with
E->Type = GetCharArrayType (GetLiteralPoolOffs () - CurTok.IVal);
E->Flags = E_LOC_LITERAL | E_RTYPE_RVAL;
E->IVal = CurTok.IVal;
- E->Name = LiteralPoolLabel;
+ E->Name = GetLiteralPoolLabel ();
NextToken ();
break;
/* Reenter the lexical level */
ReenterFunctionLevel (D);
- /* Check if the function header contains unnamed parameters. These are
+ /* Check if the function header contains unnamed parameters. These are
* only allowed in cc65 mode.
- */
+ */
if ((D->Flags & FD_UNNAMED_PARAMS) != 0 && (IS_Get (&Standard) != STD_CC65)) {
Error ("Parameter name omitted");
}
/* Allocate code and data segments for this function */
Func->V.F.Seg = PushSegments (Func);
+ /* Allocate a new literal pool */
+ PushLiteralPool (Func);
+
/* If this is a fastcall function, push the last parameter onto the stack */
if (IsQualFastcall (Func->Type) && D->ParamCount > 0) {
/* Eat the closing brace */
ConsumeRCurly ();
+ /* Dump the literal pool, the restore the old one */
+ DumpLiteralPool ();
+ PopLiteralPool ();
+
/* Switch back to the old segments */
PopSegments ();
/* */
/* */
/* */
-/* (C) 1998-2004 Ullrich von Bassewitz */
-/* Römerstraße 52 */
-/* D-70794 Filderstadt */
-/* EMail: uz@cc65.org */
+/* (C) 1998-2009, Ullrich von Bassewitz */
+/* Roemerstrasse 52 */
+/* D-70794 Filderstadt */
+/* EMail: uz@cc65.org */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* common */
#include "check.h"
+#include "coll.h"
#include "tgttrans.h"
+#include "xmalloc.h"
/* cc65 */
#include "asmlabel.h"
-unsigned LiteralPoolLabel = 0; /* Pool asm label */
-static StrBuf LiteralPool = STATIC_STRBUF_INITIALIZER;
+/* Forward for struct SymEntry */
+struct SymEntry;
+
+/* Definition of the literal pool */
+typedef struct LiteralPool LiteralPool;
+struct LiteralPool {
+ int Writable; /* True if strings are writable */
+ unsigned Label; /* Pool asm label */
+ struct SymEntry* Func; /* Function that contains the pool */
+ StrBuf Pool; /* The pool itself */
+};
+
+/* The current literal pool */
+static LiteralPool* LP = 0;
+
+/* Stack that contains the nested literal pools. Since TOS is in LiteralPool
+ * and functions aren't nested in C, the maximum depth is 1. I'm using a
+ * collection anyway, so the code is prepared for nested functions or
+ * whatever.
+ */
+static Collection LPStack = STATIC_COLLECTION_INITIALIZER;
+static LiteralPool* NewLiteralPool (struct SymEntry* Func)
+/* Create a new literal pool and return it */
+{
+ /* Allocate memory */
+ LiteralPool* LP = xmalloc (sizeof (*LP));
+
+ /* Initialize the fields */
+ LP->Writable = IS_Get (&WritableStrings);
+ LP->Label = GetLocalLabel ();
+ LP->Func = Func;
+ SB_Init (&LP->Pool);
+
+ /* Return the new pool */
+ return LP;
+}
+
+
+
+static void FreeLiteralPool (LiteralPool* LP)
+/* Free a LiteralPool structure */
+{
+ /* Free the string buffer contained within the struct */
+ SB_Done (&LP->Pool);
+
+ /* Free the struct itself */
+ xfree (LP);
+}
+
+
+
void InitLiteralPool (void)
/* Initialize the literal pool */
{
- /* Get the pool label */
- LiteralPoolLabel = GetLocalLabel ();
+ /* Create a new pool */
+ LP = NewLiteralPool (0);
+}
+
+
+
+void PushLiteralPool (struct SymEntry* Func)
+/* Push the current literal pool onto the stack and create a new one */
+{
+ /* We must have a literal pool to push! */
+ PRECONDITION (LP != 0);
+
+ /* Push the old pool */
+ CollAppend (&LPStack, LP);
+
+ /* Create a new one */
+ LP = NewLiteralPool (Func);
+}
+
+
+
+void PopLiteralPool (void)
+/* Free the current literal pool and restore the one from TOS */
+{
+ /* Free the current literal pool */
+ FreeLiteralPool (LP);
+
+ /* Pop one from stack */
+ LP = CollPop (&LPStack);
}
* charset.
*/
{
- TgtTranslateBuf (SB_GetBuf (&LiteralPool) + Offs, SB_GetLen (&LiteralPool) - Offs);
+ TgtTranslateBuf (SB_GetBuf (&LP->Pool) + Offs, SB_GetLen (&LP->Pool) - Offs);
}
/* Dump the literal pool */
{
/* If nothing there, exit... */
- if (SB_GetLen (&LiteralPool) == 0) {
+ if (SB_GetLen (&LP->Pool) == 0) {
return;
}
- /* Switch to the data segment */
- if (IS_Get (&WritableStrings)) {
+ /* Switch to the correct segment */
+ if (LP->Writable) {
g_usedata ();
} else {
g_userodata ();
}
/* Define the label */
- g_defdatalabel (LiteralPoolLabel);
+ g_defdatalabel (LP->Label);
/* Translate the buffer contents into the target charset */
TranslateLiteralPool (0);
/* Output the buffer data */
- g_defbytes (SB_GetConstBuf (&LiteralPool), SB_GetLen (&LiteralPool));
+ g_defbytes (SB_GetConstBuf (&LP->Pool), SB_GetLen (&LP->Pool));
+}
+
+
+
+unsigned GetLiteralPoolLabel (void)
+/* Return the asm label for the current literal pool */
+{
+ return LP->Label;
}
unsigned GetLiteralPoolOffs (void)
/* Return the current offset into the literal pool */
{
- return SB_GetLen (&LiteralPool);
+ return SB_GetLen (&LP->Pool);
}
* removing values from the pool.
*/
{
- CHECK (Offs <= SB_GetLen (&LiteralPool));
- SB_Cut (&LiteralPool, Offs);
+ CHECK (Offs <= SB_GetLen (&LP->Pool));
+ SB_Cut (&LP->Pool, Offs);
}
-void AddLiteralChar (char C)
-/* Add one character to the literal pool */
+unsigned AddLiteral (const char* S)
+/* Add a literal string to the literal pool. Return the starting offset into
+ * the pool
+ */
{
- SB_AppendChar (&LiteralPool, C);
+ return AddLiteralBuf (S, strlen (S) + 1);
}
-unsigned AddLiteral (const char* S)
-/* Add a literal string to the literal pool. Return the starting offset into
- * the pool
+unsigned AddLiteralBuf (const void* Buf, unsigned Len)
+/* Add a buffer containing a literal string to the literal pool. Return the
+ * starting offset into the pool for this string.
*/
{
/* Remember the starting offset */
- unsigned Start = SB_GetLen (&LiteralPool);
+ unsigned Start = SB_GetLen (&LP->Pool);
- /* Copy the string including the terminator growing the buffer if needed */
- SB_AppendBuf (&LiteralPool, S, strlen (S) + 1);
+ /* Append the buffer */
+ SB_AppendBuf (&LP->Pool, Buf, Len);
/* Return the starting offset */
return Start;
+unsigned AddLiteralStr (const StrBuf* S)
+/* Add a literal string to the literal pool. Return the starting offset into
+ * the pool for this string.
+ */
+{
+ return AddLiteralBuf (SB_GetConstBuf (S), SB_GetLen (S));
+}
+
+
+
const char* GetLiteral (unsigned Offs)
/* Get a pointer to the literal with the given offset in the pool */
{
- CHECK (Offs < SB_GetLen (&LiteralPool));
- return SB_GetConstBuf (&LiteralPool) + Offs;
+ CHECK (Offs < SB_GetLen (&LP->Pool));
+ return SB_GetConstBuf (&LP->Pool) + Offs;
}
* into Target.
*/
{
- CHECK (Offs <= SB_GetLen (&LiteralPool));
- SB_Slice (Target, &LiteralPool, Offs, SB_GetLen (&LiteralPool) - Offs);
+ CHECK (Offs <= SB_GetLen (&LP->Pool));
+ SB_Slice (Target, &LP->Pool, Offs, SB_GetLen (&LP->Pool) - Offs);
}
void PrintLiteralPoolStats (FILE* F)
/* Print statistics about the literal space used */
{
- fprintf (F, "Literal space used: %u bytes\n", SB_GetLen (&LiteralPool));
+ fprintf (F, "Literal space used: %u bytes\n", SB_GetLen (&LP->Pool));
}
/* */
/* */
/* */
-/* (C) 1998-2001 Ullrich von Bassewitz */
-/* Wacholderweg 14 */
-/* D-70597 Stuttgart */
-/* EMail: uz@cc65.org */
+/* (C) 1998-2009, Ullrich von Bassewitz */
+/* Roemerstrasse 52 */
+/* D-70794 Filderstadt */
+/* EMail: uz@cc65.org */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
-extern unsigned LiteralPoolLabel; /* Pool asm label */
+/* Forward for struct SymEntry */
+struct SymEntry;
void InitLiteralPool (void);
/* Initialize the literal pool */
+void PushLiteralPool (struct SymEntry* Func);
+/* Push the current literal pool onto the stack and create a new one */
+
+void PopLiteralPool (void);
+/* Free the current literal pool and restore the one from TOS */
+
void TranslateLiteralPool (unsigned Offs);
/* Translate the literals starting from the given offset into the target
* charset.
void DumpLiteralPool (void);
/* Dump the literal pool */
+unsigned GetLiteralPoolLabel (void);
+/* Return the asm label for the current literal pool */
+
unsigned GetLiteralPoolOffs (void);
/* Return the current offset into the literal pool */
* removing values from the pool.
*/
-void AddLiteralChar (char C);
-/* Add one character to the literal pool */
-
unsigned AddLiteral (const char* S);
/* Add a literal string to the literal pool. Return the starting offset into
* the pool for this string.
*/
+unsigned AddLiteralBuf (const void* Buf, unsigned Len);
+/* Add a buffer containing a literal string to the literal pool. Return the
+ * starting offset into the pool for this string.
+ */
+
+unsigned AddLiteralStr (const StrBuf* S);
+/* Add a literal string to the literal pool. Return the starting offset into
+ * the pool for this string.
+ */
+
const char* GetLiteral (unsigned Offs);
/* Get a pointer to the literal with the given offset in the pool */
PRAGMA_STATIC_LOCALS,
PRAGMA_STATICLOCALS, /* obsolete */
PRAGMA_WARN,
+ PRAGMA_WRITABLE_STRINGS,
PRAGMA_ZPSYM,
PRAGMA_COUNT
} pragma_t;
const char* Key; /* Keyword */
pragma_t Tok; /* Token */
} Pragmas[PRAGMA_COUNT] = {
- { "bss-name", PRAGMA_BSS_NAME },
- { "bssseg", PRAGMA_BSSSEG }, /* obsolete */
- { "charmap", PRAGMA_CHARMAP },
- { "check-stack", PRAGMA_CHECK_STACK },
- { "checkstack", PRAGMA_CHECKSTACK }, /* obsolete */
- { "code-name", PRAGMA_CODE_NAME },
- { "codeseg", PRAGMA_CODESEG }, /* obsolete */
- { "codesize", PRAGMA_CODESIZE },
- { "data-name", PRAGMA_DATA_NAME },
- { "dataseg", PRAGMA_DATASEG }, /* obsolete */
- { "optimize", PRAGMA_OPTIMIZE },
- { "register-vars", PRAGMA_REGISTER_VARS },
- { "regvaraddr", PRAGMA_REGVARADDR },
- { "regvars", PRAGMA_REGVARS }, /* obsolete */
- { "rodata-name", PRAGMA_RODATA_NAME },
- { "rodataseg", PRAGMA_RODATASEG }, /* obsolete */
- { "signed-chars", PRAGMA_SIGNED_CHARS },
- { "signedchars", PRAGMA_SIGNEDCHARS }, /* obsolete */
- { "static-locals", PRAGMA_STATIC_LOCALS },
- { "staticlocals", PRAGMA_STATICLOCALS }, /* obsolete */
- { "warn", PRAGMA_WARN },
- { "zpsym", PRAGMA_ZPSYM },
+ { "bss-name", PRAGMA_BSS_NAME },
+ { "bssseg", PRAGMA_BSSSEG }, /* obsolete */
+ { "charmap", PRAGMA_CHARMAP },
+ { "check-stack", PRAGMA_CHECK_STACK },
+ { "checkstack", PRAGMA_CHECKSTACK }, /* obsolete */
+ { "code-name", PRAGMA_CODE_NAME },
+ { "codeseg", PRAGMA_CODESEG }, /* obsolete */
+ { "codesize", PRAGMA_CODESIZE },
+ { "data-name", PRAGMA_DATA_NAME },
+ { "dataseg", PRAGMA_DATASEG }, /* obsolete */
+ { "optimize", PRAGMA_OPTIMIZE },
+ { "register-vars", PRAGMA_REGISTER_VARS },
+ { "regvaraddr", PRAGMA_REGVARADDR },
+ { "regvars", PRAGMA_REGVARS }, /* obsolete */
+ { "rodata-name", PRAGMA_RODATA_NAME },
+ { "rodataseg", PRAGMA_RODATASEG }, /* obsolete */
+ { "signed-chars", PRAGMA_SIGNED_CHARS },
+ { "signedchars", PRAGMA_SIGNEDCHARS }, /* obsolete */
+ { "static-locals", PRAGMA_STATIC_LOCALS },
+ { "staticlocals", PRAGMA_STATICLOCALS }, /* obsolete */
+ { "warn", PRAGMA_WARN },
+ { "writable-strings", PRAGMA_WRITABLE_STRINGS },
+ { "zpsym", PRAGMA_ZPSYM },
};
/* Result of ParsePushPop */
WarnPragma (&B);
break;
+ case PRAGMA_WRITABLE_STRINGS:
+ FlagPragma (&B, &WritableStrings);
+ break;
+
case PRAGMA_ZPSYM:
StringPragma (&B, MakeZPSym);
break;
static void StringConst (void)
/* Parse a quoted string */
-{
- NextTok.IVal = GetLiteralPoolOffs ();
- NextTok.Tok = TOK_SCONST;
+{
+ /* String buffer */
+ StrBuf S = AUTO_STRBUF_INITIALIZER;
/* Concatenate strings. If at least one of the concenated strings is a wide
* character literal, the whole string is a wide char literal, otherwise
Error ("Unexpected newline");
break;
}
- AddLiteralChar (ParseChar ());
+ SB_AppendChar (&S, ParseChar ());
}
/* Skip closing quote char if there was one */
}
/* Terminate the string */
- AddLiteralChar ('\0');
+ SB_AppendChar (&S, '\0');
+
+ /* Add the whole string to the literal pool */
+ NextTok.IVal = AddLiteralStr (&S);
+ NextTok.Tok = TOK_SCONST;
+
+ /* Free the buffer */
+ SB_Done (&S);
}