/*****************************************************************************/
/*                                                                           */
/*				   objcode.c				     */
/*                                                                           */
/*	       Objectcode management for the ca65 macroassembler	     */
/*                                                                           */
/*                                                                           */
/*                                                                           */
/* (C) 1998-2001 Ullrich von Bassewitz                                       */
/*               Wacholderweg 14                                             */
/*               D-70597 Stuttgart                                           */
/* 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 <string.h>
#include <errno.h>

/* common */
#include "chartype.h"
#include "check.h"
#include "segdefs.h"
#include "xmalloc.h"

/* cc65 */
#include "error.h"
#include "fragment.h"
#include "global.h"
#include "lineinfo.h"
#include "listing.h"
#include "objfile.h"
#include "scanner.h"
#include "symtab.h"
#include "objcode.h"



/*****************************************************************************/
/*     	      	    		     Data				     */
/*****************************************************************************/



/* Are we in absolute mode or in relocatable mode? */
int 		RelocMode = 1;
unsigned long	AbsPC	  = 0;		/* PC if in absolute mode */



typedef struct Segment_ Segment;
struct Segment_ {
    Segment*   	    List;      	       	/* List of all segments */
    Fragment*  	    Root;	  	/* Root of fragment list */
    Fragment*  	    Last;	  	/* Pointer to last fragment */
    unsigned char   Align;		/* Segment alignment */
    unsigned char   SegType;   	       	/* True if zero page segment */
    unsigned long   PC;
    unsigned   	    Num;  		/* Segment number */
    char*      	    Name;		/* Segment name */
};



/* Predefined segments */
static Segment NullSeg = {
    0, 0, 0, 0, SEGTYPE_ABS, 0, 5, "NULL"
};
static Segment ZeropageSeg = {
    &NullSeg, 0, 0, 0, SEGTYPE_ZP, 0, 4, "ZEROPAGE"
};
static Segment DataSeg = {
    &ZeropageSeg, 0, 0, 0, SEGTYPE_ABS, 0, 3, "DATA"
};
static Segment BssSeg = {
    &DataSeg, 0, 0, 0, SEGTYPE_ABS, 0, 2, "BSS"
};
static Segment RODataSeg = {
    &BssSeg, 0, 0, 0, SEGTYPE_ABS, 0, 1, "RODATA"
};
static Segment CodeSeg = {
    &RODataSeg, 0, 0, 0, SEGTYPE_ABS, 0, 0, "CODE"
};

/* Number of segments */
static unsigned SegmentCount = 6;

/* List of all segments */
static Segment* SegmentList = &CodeSeg;
static Segment* SegmentLast = &NullSeg;

/* Currently active segment */
static Segment*	ActiveSeg = &CodeSeg;



/*****************************************************************************/
/*     	       	   	      Segment management			     */
/*****************************************************************************/



static Segment* NewSegment (const char* Name, unsigned SegType)
/* Create a new segment, insert it into the global list and return it */
{
    Segment* S;
    const char* N;

    /* Check for too many segments */
    if (SegmentCount >= 256) {
     	Fatal (FAT_TOO_MANY_SEGMENTS);
    }

    /* Check the segment name for invalid names */
    N = Name;
    if ((*N != '_' && !IsAlpha (*N)) || strlen (Name) > 80) {
     	Error (ERR_ILLEGAL_SEGMENT, Name);
    }
    do {
     	if (*N != '_' && !IsAlNum (*N)) {
     	    Error (ERR_ILLEGAL_SEGMENT, Name);
     	    break;
     	}
     	++N;
    } while (*N);

    /* Create a new segment */
    S = xmalloc (sizeof (*S));

    /* Initialize it */
    S->List    = 0;
    S->Root    = 0;
    S->Last    = 0;
    S->Align   = 0;
    S->SegType = SegType;
    S->PC      = 0;
    S->Num     = SegmentCount++;
    S->Name    = xstrdup (Name);

    /* Insert it into the segment list */
    SegmentLast->List = S;
    SegmentLast = S;

    /* And return it... */
    return S;
}



void UseCodeSeg (void)
/* Use the code segment */
{
    ActiveSeg = &CodeSeg;
}



void UseRODataSeg (void)
/* Use the r/o data segment */
{
    ActiveSeg = &RODataSeg;
}



void UseDataSeg (void)
/* Use the data segment */
{
    ActiveSeg = &DataSeg;
}



void UseBssSeg (void)
/* Use the BSS segment */
{
    ActiveSeg = &BssSeg;
}



void UseZeropageSeg (void)
/* Use the zero page segment */
{
    ActiveSeg = &ZeropageSeg;
}



void UseNullSeg (void)
/* Use the null segment */
{
    ActiveSeg = &NullSeg;
}



void UseSeg (const char* Name, unsigned SegType)
/* Use the segment with the given name */
{
    Segment* Seg = SegmentList;
    while (Seg) {
     	if (strcmp (Seg->Name, Name) == 0) {
     	    /* We found this segment. Check if the type is identical */
	    if (SegType != SEGTYPE_DEFAULT && Seg->SegType != SegType) {
		Error (ERR_SEG_ATTR_MISMATCH);
		/* Use the new attribute to avoid errors */
	        Seg->SegType = SegType;
       	    }
       	    ActiveSeg = Seg;
     	    return;
     	}
     	/* Check next segment */
     	Seg = Seg->List;
    }

    /* Segment is not in list, create a new one */
    if (SegType == SEGTYPE_DEFAULT) {
     	SegType = SEGTYPE_ABS;
    }
    Seg = NewSegment (Name, SegType);
    ActiveSeg = Seg;
}



unsigned long GetPC (void)
/* Get the program counter of the current segment */
{
    return RelocMode? ActiveSeg->PC : AbsPC;
}



void SetAbsPC (unsigned long PC)
/* Set the program counter in absolute mode */
{
    RelocMode = 0;
    AbsPC = PC;
}



unsigned GetSegNum (void)
/* Get the number of the current segment */
{
    return ActiveSeg->Num;
}



void SegAlign (unsigned Power, int Val)
/* Align the PC segment to 2^Power. If Val is -1, emit fill fragments (the
 * actual fill value will be determined by the linker), otherwise use the
 * given value.
 */
{
    unsigned char Data [4];
    unsigned long Align = (1UL << Power) - 1;
    unsigned long NewPC = (ActiveSeg->PC + Align) & ~Align;
    unsigned long Count = NewPC - ActiveSeg->PC;

    if (Val != -1) {
	/* User defined fill value */
	memset (Data, Val, sizeof (Data));
	while (Count) {
	    if (Count > sizeof (Data)) {
	    	EmitData (Data, sizeof (Data));
	    	Count -= sizeof (Data);
	    } else {
	    	EmitData (Data, Count);
	    	Count = 0;
	    }
	}
    } else {
	/* Linker defined fill value */
	EmitFill (Count);
    }

    /* Remember the alignment in the header */
    if (ActiveSeg->Align < Power) {
     	ActiveSeg->Align = Power;
    }
}



int IsZPSeg (void)
/* Return true if the current segment is a zeropage segment */
{
    return (ActiveSeg->SegType == SEGTYPE_ZP);
}



int IsFarSeg (void)
/* Return true if the current segment is a far segment */
{
    return (ActiveSeg->SegType == SEGTYPE_FAR);
}



unsigned GetSegType (unsigned SegNum)
/* Return the type of the segment with the given number */
{
    /* Search for the segment */
    Segment* S = SegmentList;
    while (S && SegNum) {
    	--SegNum;
    	S = S->List;
    }

    /* Did we find it? */
    if (S == 0) {
    	FAIL ("Invalid segment number");
    }

    /* Return the segment type */
    return S->SegType;
}



void SegCheck (void)
/* Check the segments for range and other errors */
{
    Segment* S = SegmentList;
    while (S) {
     	Fragment* F = S->Root;
     	while (F) {
       	    if (F->Type == FRAG_EXPR || F->Type == FRAG_SEXPR) {
       	       	F->V.Expr = FinalizeExpr (F->V.Expr);
       	       	if (IsConstExpr (F->V.Expr)) {
     	       	    /* We are able to evaluate the expression. Get the value
     	       	     * and check for range errors.
     	       	     */
     	       	    unsigned I;
     	       	    long Val = GetExprVal (F->V.Expr);
     	       	    int Abs = (F->Type != FRAG_SEXPR);

     	       	    if (F->Len == 1) {
     	       		if (Abs) {
     	       		    /* Absolute value */
     	       		    if (Val > 255) {
     	       	       	     	PError (&F->Pos, ERR_RANGE);
     	       		    }
     	       		} else {
     	     		    /* PC relative value */
     	     		    if (Val < -128 || Val > 127) {
     	     	       	     	PError (&F->Pos, ERR_RANGE);
     	     		    }
     	     		}
     	       	    } else if (F->Len == 2) {
     	     	    	if (Abs) {
     	     		    /* Absolute value */
     	     		    if (Val > 65535) {
     	       	       	     	PError (&F->Pos, ERR_RANGE);
     	     		    }
    	     		} else {
	     		    /* PC relative value */
	     		    if (Val < -32768 || Val > 32767) {
	     	       	     	PError (&F->Pos, ERR_RANGE);
	       		    }
	     		}
	     	    }

	     	    /* Convert the fragment into a literal fragment */
	     	    for (I = 0; I < F->Len; ++I) {
	     	       	F->V.Data [I] = Val & 0xFF;
	     	       	Val >>= 8;
     	     	    }
	     	    F->Type = FRAG_LITERAL;
	     	} else {
	     	    /* We cannot evaluate the expression now, leave the job for
	     	     * the linker. However, we are able to check for explicit
	     	     * byte expressions and we will do so.
		     */
	       	    if (F->Type == FRAG_EXPR && F->Len == 1 && !IsByteExpr (F->V.Expr)) {
	       	        PError (&F->Pos, ERR_RANGE);
	     	    }
		}
	    }
	    F = F->Next;
	}
    	S = S->List;
    }
}



void SegDump (void)
/* Dump the contents of all segments */
{
    unsigned X = 0;
    Segment* S = SegmentList;
    printf ("\n");
    while (S) {
	unsigned I;
	Fragment* F;
	int State = -1;
       	printf ("New segment: %s", S->Name);
	F = S->Root;
	while (F) {
    	    if (F->Type == FRAG_LITERAL) {
	       	if (State != 0) {
	       	    printf ("\n  Literal:");
		    X = 15;
	       	    State = 0;
		}
		for (I = 0; I < F->Len; ++I) {
		    printf (" %02X", F->V.Data [I]);
		    X += 3;
		}
     	    } else if (F->Type == FRAG_EXPR || F->Type == FRAG_SEXPR) {
       	       	State = 1;
	      	printf ("\n  Expression (%u): ", F->Len);
		DumpExpr (F->V.Expr);
	    } else if (F->Type == FRAG_FILL) {
		State = 1;
		printf ("\n  Fill bytes (%u)", F->Len);
	    } else {
		Internal ("Unknown fragment type: %u", F->Type);
	    }
       	    if (X > 65) {
	       	State = -1;
	    }
	    F = F->Next;
	}
	printf ("\n  End PC = $%04X\n", (unsigned)(S->PC & 0xFFFF));
	S = S->List;
    }
    printf ("\n");
}



static void WriteOneSeg (Segment* Seg)
/* Write one segment to the object file */
{
    Fragment* Frag;
    unsigned LineInfoIndex;

    /* Write the segment name followed by the byte count in this segment */
    ObjWriteStr (Seg->Name);
    ObjWrite32 (Seg->PC);
    ObjWrite8 (Seg->Align);
    ObjWrite8 (Seg->SegType);

    /* Now walk through the fragment list for this segment and write the
     * fragments.
     */
    Frag = Seg->Root;
    while (Frag) {

    	/* Write data depending on the type */
       	switch (Frag->Type) {

    	    case FRAG_LITERAL:
		ObjWrite8 (FRAG_LITERAL);
		ObjWriteVar (Frag->Len);
	        ObjWriteData (Frag->V.Data, Frag->Len);
    		break;

    	    case FRAG_EXPR:
    		switch (Frag->Len) {
    		    case 1:   ObjWrite8 (FRAG_EXPR8);   break;
    		    case 2:   ObjWrite8 (FRAG_EXPR16);	break;
    		    case 3:   ObjWrite8 (FRAG_EXPR24);	break;
    		    case 4:   ObjWrite8 (FRAG_EXPR32);	break;
    		    default:  Internal ("Invalid fragment size: %u", Frag->Len);
     		}
    		WriteExpr (Frag->V.Expr);
    		break;

    	    case FRAG_SEXPR:
    		switch (Frag->Len) {
    		    case 1:   ObjWrite8 (FRAG_SEXPR8);  break;
    		    case 2:   ObjWrite8 (FRAG_SEXPR16);	break;
    		    case 3:   ObjWrite8 (FRAG_SEXPR24);	break;
    		    case 4:   ObjWrite8 (FRAG_SEXPR32);	break;
    		    default:  Internal ("Invalid fragment size: %u", Frag->Len);
    		}
    		WriteExpr (Frag->V.Expr);
    		break;

	    case FRAG_FILL:
		ObjWrite8 (FRAG_FILL);
       	       	ObjWriteVar (Frag->Len);
		break;

    	    default:
    	 	Internal ("Invalid fragment type: %u", Frag->Type);

    	}

       	/* Write the file position of this fragment */
	ObjWritePos (&Frag->Pos);

	/* Write extra line info for this fragment. Zero is considered
	 * "no line info", so add one to the value.
	 */
	LineInfoIndex = Frag->LI? Frag->LI->Index + 1 : 0;
	ObjWriteVar (LineInfoIndex);

	/* Next fragment */
	Frag = Frag->Next;
    }
}



void WriteSegments (void)
/* Write the segment data to the object file */
{
    Segment* Seg;

    /* Tell the object file module that we're about to start the seg list */
    ObjStartSegments ();

    /* First thing is segment count */
    ObjWriteVar (SegmentCount);

    /* Now walk through all segments and write them to the object file */
    Seg = SegmentList;
    while (Seg) {
	/* Write one segment */
	WriteOneSeg (Seg);
	/* Next segment */
      	Seg = Seg->List;
    }

    /* Done writing segments */
    ObjEndSegments ();
}



/*****************************************************************************/
/*     	      	      	   	     Code				     */
/*****************************************************************************/



static void IncPC (unsigned Value)
/* Increment the PC counter */
{
    ActiveSeg->PC += Value;
    if (!RelocMode) {
	AbsPC += Value;
    }
}



static Fragment* NewFragment (unsigned char Type, unsigned short Len)
/* Create, initialize and return a new fragment. The fragment will be inserted
 * into the current segment.
 */
{
    Fragment* F;

    /* Create a new fragment */
    F = xmalloc (sizeof (*F));

    /* Initialize it */
    F->List 	= 0;
    F->Next 	= 0;
    F->LineList = 0;
    F->Pos  	= CurPos;
    F->LI       = UseLineInfo (CurLineInfo);
    F->Len  	= Len;
    F->Type 	= Type;

    /* Insert it into the list of all segments */
    if (FragList == 0) {
    	FragList = F;
    } else {
    	FragLast->List = F;
    }
    FragLast = F;

    /* Insert it into the current segment */
    if (ActiveSeg->Root) {
    	ActiveSeg->Last->Next = F;
    	ActiveSeg->Last = F;
    } else {
    	ActiveSeg->Root = ActiveSeg->Last = F;
    }

    /* Add this fragment to the current listing line */
    if (LineCur) {
	if (LineCur->FragList == 0) {
	    LineCur->FragList = F;
	} else {
       	    LineCur->FragLast->LineList = F;
     	}
     	LineCur->FragLast = F;
    }

    /* Increment the program counter */
    IncPC (Len);

    /* And return it */
    return F;
}



void Emit0 (unsigned char OPC)
/* Emit an instruction with a zero sized operand */
{
    /* First fragment, wrong type or out of space, create new one */
    Fragment* F = NewFragment (FRAG_LITERAL, 1);
    F->V.Data [0] = OPC;
}



void Emit1 (unsigned char OPC, ExprNode* Value)
/* Emit an instruction with an one byte argument */
{
    Emit0 (OPC);
    EmitByte (Value);
}



void Emit2 (unsigned char OPC, ExprNode* Value)
/* Emit an instruction with a two byte argument */
{
    Emit0 (OPC);
    EmitWord (Value);
}



void Emit3 (unsigned char OPC, ExprNode* Expr)
/* Emit an instruction with a three byte argument */
{
    Emit0 (OPC);
    EmitFarAddr (Expr);
}



void Emit3b (unsigned char OPC, ExprNode* Expr, ExprNode* Bank)
/* Emit an instruction with a three byte argument and separate bank */
{
    Emit0 (OPC);
    EmitWord (Expr);
    EmitByte (Bank);
}



void EmitPCRel (unsigned char OPC, ExprNode* Expr, unsigned Size)
/* Emit an opcode with a PC relative argument of one or two bytes */
{
    Fragment* F;
    Emit0 (OPC);
    F = NewFragment (FRAG_SEXPR, Size);
    F->V.Expr = Expr;
}



void EmitData (const unsigned char* Data, unsigned Size)
/* Emit data into the current segment */
{
    /* Create lots of fragments for the data */
    while (Size) {
	Fragment* F;

	/* Determine the length of the next fragment */
	unsigned Len = Size;
       	if (Len > sizeof (F->V.Data)) {
     	    Len = sizeof (F->V.Data);
       	}

     	/* Create a new fragment */
     	F = NewFragment (FRAG_LITERAL, Len);

     	/* Copy the data */
     	memcpy (F->V.Data, Data, Len);

     	/* Next chunk */
     	Data += Len;
     	Size -= Len;

    }
}



void EmitByte (ExprNode* Expr)
/* Emit one byte */
{
    if (IsConstExpr (Expr)) {
     	/* Constant expression, emit literal byte */
     	long Val = GetExprVal (Expr);
	FreeExpr (Expr);
     	if ((Val & ~0xFF) != 0) {
     	    Error (ERR_RANGE);
     	}
     	Emit0 (Val & 0xFF);
    } else {
     	/* Create a new fragment */
     	Fragment* F = NewFragment (FRAG_EXPR, 1);

     	/* Set the data */
     	F->V.Expr = Expr;
    }
}



void EmitWord (ExprNode* Expr)
/* Emit one word */
{
    if (IsConstExpr (Expr)) {
     	/* Constant expression, emit literal byte */
     	long Val = GetExprVal (Expr);
	FreeExpr (Expr);
       	if ((Val & ~0xFFFF) != 0) {
     	    Error (ERR_RANGE);
     	}
     	Emit0 (Val & 0xFF);
	Emit0 ((Val >> 8) & 0xFF);
    } else {
     	/* Create a new fragment */
     	Fragment* F = NewFragment (FRAG_EXPR, 2);

     	/* Set the data */
     	F->V.Expr = Expr;
    }
}



void EmitFarAddr (ExprNode* Expr)
/* Emit a 24 bit expression */
{
    if (IsConstExpr (Expr)) {
     	/* Constant expression, emit literal byte */
     	long Val = GetExprVal (Expr);
	FreeExpr (Expr);
       	if ((Val & ~0xFFFFFF) != 0) {
     	    Error (ERR_RANGE);
     	}
     	Emit0 (Val & 0xFF);
	Emit0 ((Val >> 8) & 0xFF);
	Emit0 ((Val >> 16) & 0xFF);
    } else {
     	/* Create a new fragment */
     	Fragment* F = NewFragment (FRAG_EXPR, 3);

     	/* Set the data */
     	F->V.Expr = Expr;
    }
}



void EmitDWord (ExprNode* Expr)
/* Emit one dword */
{
    if (IsConstExpr (Expr)) {
     	/* Constant expression, emit literal byte */
     	long Val = GetExprVal (Expr);
	FreeExpr (Expr);
     	Emit0 (Val & 0xFF);
	Emit0 ((Val >> 8) & 0xFF);
       	Emit0 ((Val >> 16) & 0xFF);
	Emit0 ((Val >> 24) & 0xFF);
    } else {
     	/* Create a new fragment */
     	Fragment* F = NewFragment (FRAG_EXPR, 4);

     	/* Set the data */
     	F->V.Expr = Expr;
    }
}



void EmitFill (unsigned long Count)
/* Emit Count fill bytes */
{
    while (Count) {
	/* Calculate the size of the next chunk */
	unsigned Chunk = (Count > 0xFFFF)? 0xFFFF : (unsigned) Count;
	Count -= Chunk;

	/* Emit one chunk */
	NewFragment (FRAG_FILL, Chunk);
    }
}



