cc65/src/cc65/segments.c

401 lines
11 KiB
C

/*****************************************************************************/
/* */
/* segments.c */
/* */
/* Lightweight segment management stuff */
/* */
/* */
/* */
/* (C) 2001-2009, Ullrich von Bassewitz */
/* Roemerstrasse 52 */
/* D-70794 Filderstadt */
/* EMail: uz@cc65.org */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
/* arising from the use of this software. */
/* */
/* Permission is granted to anyone to use this software for any purpose, */
/* including commercial applications, and to alter it and redistribute it */
/* freely, subject to the following restrictions: */
/* */
/* 1. The origin of this software must not be misrepresented; you must not */
/* claim that you wrote the original software. If you use this software */
/* in a product, an acknowledgment in the product documentation would be */
/* appreciated but is not required. */
/* 2. Altered source versions must be plainly marked as such, and must not */
/* be misrepresented as being the original software. */
/* 3. This notice may not be removed or altered from any source */
/* distribution. */
/* */
/*****************************************************************************/
#include <stdarg.h>
#include <string.h>
/* common */
#include "addrsize.h"
#include "chartype.h"
#include "check.h"
#include "coll.h"
#include "scanner.h"
#include "segnames.h"
#include "strstack.h"
#include "xmalloc.h"
/* cc65 */
#include "codeent.h"
#include "codeseg.h"
#include "dataseg.h"
#include "error.h"
#include "textseg.h"
#include "segments.h"
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* Table struct for address sizes of segments */
typedef struct {
StrBuf Name;
unsigned char AddrSize;
} SegAddrSize_t;
/* Pointer to the current segment context. Output goes here. */
SegContext* CS = 0;
/* Pointer to the global segment context */
SegContext* GS = 0;
/* Actual names for the segments */
static StrStack SegmentNames[SEG_COUNT];
/* Address size for the segments */
static Collection SegmentAddrSizes;
/* We're using a collection for the stack instead of a linked list. Since
** functions may not be nested (at least in the current implementation), the
** maximum stack depth is 2, so there is not really a need for a better
** implementation.
*/
static Collection SegContextStack = STATIC_COLLECTION_INITIALIZER;
/*****************************************************************************/
/* Segment name and address size */
/*****************************************************************************/
void InitSegAddrSizes (void)
/* Initialize the segment address sizes */
{
InitCollection (&SegmentAddrSizes);
}
void DoneSegAddrSizes (void)
/* Free the segment address sizes */
{
SegAddrSize_t* A;
int I;
for (I = 0; I < (int)CollCount (&SegmentAddrSizes); ++I) {
A = CollAtUnchecked (&SegmentAddrSizes, I);
SB_Done (&A->Name);
xfree (A);
}
DoneCollection (&SegmentAddrSizes);
}
static SegAddrSize_t* FindSegAddrSize (const char* Name)
/* Find already specified address size for a segment by name.
** Return the found struct or 0 if not found.
*/
{
SegAddrSize_t* A;
int I;
for (I = 0; I < (int)CollCount (&SegmentAddrSizes); ++I) {
A = CollAtUnchecked (&SegmentAddrSizes, I);
if (A && strcmp (SB_GetConstBuf (&A->Name), Name) == 0) {
return A;
}
}
return 0;
}
void SetSegAddrSize (const char* Name, unsigned char AddrSize)
/* Set the address size for a segment */
{
SegAddrSize_t* A = FindSegAddrSize (Name);
if (!A) {
/* New one */
A = xmalloc (sizeof (SegAddrSize_t));
SB_Init (&A->Name);
SB_CopyStr (&A->Name, Name);
SB_Terminate (&A->Name);
CollAppend (&SegmentAddrSizes, A);
} else {
/* Check for mismatching address sizes */
if (A->AddrSize != AddrSize) {
Warning ("Segment address size changed from last time!");
}
}
/* Set the address size anyway */
A->AddrSize = AddrSize;
}
unsigned char GetSegAddrSize (const char* Name)
/* Get the address size of the given segment.
** Return ADDR_SIZE_INVALID if not found.
*/
{
SegAddrSize_t* A = FindSegAddrSize (Name);
if (A) {
return A->AddrSize;
}
return ADDR_SIZE_INVALID;
}
void InitSegNames (void)
/* Initialize the segment names */
{
SS_Push (&SegmentNames[SEG_BSS], SEGNAME_BSS);
SS_Push (&SegmentNames[SEG_CODE], SEGNAME_CODE);
SS_Push (&SegmentNames[SEG_DATA], SEGNAME_DATA);
SS_Push (&SegmentNames[SEG_RODATA], SEGNAME_RODATA);
}
void SetSegName (segment_t Seg, const char* Name)
/* Set a new name for a segment */
{
SS_Set (&SegmentNames[Seg], Name);
}
void PushSegName (segment_t Seg, const char* Name)
/* Push the current segment name and set a new name for a segment */
{
if (SS_IsFull (&SegmentNames[Seg])) {
Error ("Segment name stack overflow");
} else {
SS_Push (&SegmentNames[Seg], Name);
}
}
void PopSegName (segment_t Seg)
/* Restore a segment name from the segment name stack */
{
if (SS_GetCount (&SegmentNames[Seg]) < 2) {
Error ("Segment name stack is empty");
} else {
SS_Drop (&SegmentNames[Seg]);
}
}
const char* GetSegName (segment_t Seg)
/* Get the name of the given segment */
{
return SS_Get (&SegmentNames[Seg]);
}
/*****************************************************************************/
/* Segment context */
/*****************************************************************************/
static SegContext* NewSegContext (SymEntry* Func)
/* Initialize a SegContext structure (set all fields to NULL) */
{
/* Allocate memory */
SegContext* S = xmalloc (sizeof (SegContext));
/* Initialize the fields */
S->Text = NewTextSeg (Func);
S->Code = NewCodeSeg (GetSegName (SEG_CODE), Func);
S->Data = NewDataSeg (GetSegName (SEG_DATA), Func);
S->ROData = NewDataSeg (GetSegName (SEG_RODATA), Func);
S->BSS = NewDataSeg (GetSegName (SEG_BSS), Func);
S->CurDSeg = SEG_DATA;
S->NextLabel = 0;
S->NextDataLabel = 0;
/* Return the new struct */
return S;
}
SegContext* PushSegContext (SymEntry* Func)
/* Make the new segment context current but remember the old one */
{
/* Push the current pointer onto the stack */
CollAppend (&SegContextStack, CS);
/* Create a new SegContext structure */
CS = NewSegContext (Func);
/* Return the new struct */
return CS;
}
void PopSegContext (void)
/* Pop the old segment context (make it current) */
{
/* Must have something on the stack */
PRECONDITION (CollCount (&SegContextStack) > 0);
/* Pop the last segment and set it as current */
CS = CollPop (&SegContextStack);
}
void CreateGlobalSegments (void)
/* Create the global segments and remember them in GS */
{
GS = PushSegContext (0);
}
void UseDataSeg (segment_t DSeg)
/* For the current segment context, use the data segment DSeg */
{
/* Check the input */
PRECONDITION (CS && DSeg != SEG_CODE);
/* Set the new segment to use */
CS->CurDSeg = DSeg;
}
struct DataSeg* GetDataSeg (void)
/* Return the current data segment */
{
PRECONDITION (CS != 0);
switch (CS->CurDSeg) {
case SEG_BSS: return CS->BSS;
case SEG_DATA: return CS->Data;
case SEG_RODATA: return CS->ROData;
default:
FAIL ("Invalid data segment");
return 0;
}
}
void AddTextLine (const char* Format, ...)
/* Add a line of code to the current text segment */
{
va_list ap;
va_start (ap, Format);
CHECK (CS != 0);
TS_AddVLine (CS->Text, Format, ap);
va_end (ap);
}
void AddCodeLine (const char* Format, ...)
/* Add a line of code to the current code segment */
{
va_list ap;
va_start (ap, Format);
CHECK (CS != 0);
CS_AddVLine (CS->Code, CurTok.LI, Format, ap);
va_end (ap);
}
void AddCode (opc_t OPC, am_t AM, const char* Arg, struct CodeLabel* JumpTo)
/* Add a code entry to the current code segment */
{
CHECK (CS != 0);
CS_AddEntry (CS->Code, NewCodeEntry (OPC, AM, Arg, JumpTo, CurTok.LI));
}
void AddDataLine (const char* Format, ...)
/* Add a line of data to the current data segment */
{
va_list ap;
va_start (ap, Format);
CHECK (CS != 0);
DS_AddVLine (GetDataSeg(), Format, ap);
va_end (ap);
}
int HaveGlobalCode (void)
/* Return true if the global code segment contains entries (which is an error) */
{
return (CS_GetEntryCount (GS->Code) > 0);
}
void RemoveGlobalCode (void)
/* Remove all code from the global code segment. Used for error recovery. */
{
CS_DelEntries (GS->Code, 0, CS_GetEntryCount (GS->Code));
}
void OutputSegments (const SegContext* S)
/* Output the given segments to the output file */
{
/* Output the function prologue if the segments came from a function */
CS_OutputPrologue (S->Code);
/* Output the text segment */
TS_Output (S->Text);
/* Output the code segment */
CS_Output (S->Code);
/* Output the three data segments */
DS_Output (S->Data);
DS_Output (S->ROData);
DS_Output (S->BSS);
/* Output the code segment epiloque */
CS_OutputEpilogue (S->Code);
}