shithub: rgbds

ref: 2fa9a3c7683ff45119307554fa71d268039cca3c
dir: /src/asm/output.c/

View raw version
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * INCLUDES
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "asm/asm.h"
#include "asm/output.h"
#include "asm/symbol.h"
#include "asm/mylink.h"
#include "asm/main.h"
#include "asm/rpn.h"
#include "asm/fstack.h"

#define SECTIONCHUNK	0x4000

/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Internal structures
 *
 */

void out_SetCurrentSection(struct Section * pSect);

struct Patch {
	char tzFilename[_MAX_PATH + 1];
	ULONG nLine;
	ULONG nOffset;
	UBYTE nType;
	ULONG nRPNSize;
	UBYTE *pRPN;
	struct Patch *pNext;
};

struct PatchSymbol {
	ULONG ID;
	struct sSymbol *pSymbol;
	struct PatchSymbol *pNext;
};

struct SectionStackEntry {
	struct Section *pSection;
	struct SectionStackEntry *pNext;
};
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * VARIABLES
 *
 */

struct Section *pSectionList = NULL, *pCurrentSection = NULL;
struct PatchSymbol *pPatchSymbols = NULL;
char tzObjectname[_MAX_PATH];
struct SectionStackEntry *pSectionStack = NULL;

/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Section stack routines
 *
 */

void 
out_PushSection(void)
{
	struct SectionStackEntry *pSect;

	if ((pSect =
		(struct SectionStackEntry *)
		malloc(sizeof(struct SectionStackEntry))) != NULL) {
		pSect->pSection = pCurrentSection;
		pSect->pNext = pSectionStack;
		pSectionStack = pSect;
	} else
		fatalerror("No memory for section stack");
}

void 
out_PopSection(void)
{
	if (pSectionStack) {
		struct SectionStackEntry *pSect;

		pSect = pSectionStack;
		out_SetCurrentSection(pSect->pSection);
		pSectionStack = pSect->pNext;
		free(pSect);
	} else
		fatalerror("No entries in the section stack");
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Count the number of symbols used in this object
 *
 */

ULONG 
countsymbols(void)
{
	struct PatchSymbol *pSym;
	ULONG count = 0;

	pSym = pPatchSymbols;

	while (pSym) {
		count += 1;
		pSym = pSym->pNext;
	}

	return (count);
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Count the number of sections used in this object
 *
 */

ULONG 
countsections(void)
{
	struct Section *pSect;
	ULONG count = 0;

	pSect = pSectionList;

	while (pSect) {
		count += 1;
		pSect = pSect->pNext;
	}

	return (count);
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Count the number of patches used in this object
 *
 */

ULONG 
countpatches(struct Section * pSect)
{
	struct Patch *pPatch;
	ULONG r = 0;

	pPatch = pSect->pPatches;
	while (pPatch) {
		r += 1;
		pPatch = pPatch->pNext;
	}

	return (r);
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Write a long to a file (little-endian)
 *
 */

void 
fputlong(ULONG i, FILE * f)
{
	fputc(i, f);
	fputc(i >> 8, f);
	fputc(i >> 16, f);
	fputc(i >> 24, f);
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Write a NULL-terminated string to a file
 *
 */

void 
fputstring(char *s, FILE * f)
{
	while (*s)
		fputc(*s++, f);
	fputc(0, f);
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Return a sections ID
 *
 */

ULONG 
getsectid(struct Section * pSect)
{
	struct Section *sec;
	ULONG ID = 0;

	sec = pSectionList;

	while (sec) {
		if (sec == pSect)
			return (ID);
		ID += 1;
		sec = sec->pNext;
	}

	fatalerror("INTERNAL: Unknown section");
	return ((ULONG) - 1);
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Write a patch to a file
 *
 */

void 
writepatch(struct Patch * pPatch, FILE * f)
{
	fputstring(pPatch->tzFilename, f);
	fputlong(pPatch->nLine, f);
	fputlong(pPatch->nOffset, f);
	fputc(pPatch->nType, f);
	fputlong(pPatch->nRPNSize, f);
	fwrite(pPatch->pRPN, 1, pPatch->nRPNSize, f);
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Write a section to a file
 *
 */

void 
writesection(struct Section * pSect, FILE * f)
{
	//printf("SECTION: %s, ID: %d\n", pSect->pzName, getsectid(pSect));

	fputlong(pSect->nPC, f);
	fputc(pSect->nType, f);
	fputlong(pSect->nOrg, f);
	//RGB1 addition

	    fputlong(pSect->nBank, f);
	//RGB1 addition

	    if ((pSect->nType == SECT_HOME)
	    || (pSect->nType == SECT_CODE)) {
		struct Patch *pPatch;

		fwrite(pSect->tData, 1, pSect->nPC, f);
		fputlong(countpatches(pSect), f);

		pPatch = pSect->pPatches;
		while (pPatch) {
			writepatch(pPatch, f);
			pPatch = pPatch->pNext;
		}
	}
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Write a symbol to a file
 *
 */

void 
writesymbol(struct sSymbol * pSym, FILE * f)
{
	char symname[MAXSYMLEN * 2 + 1];
	ULONG type;
	ULONG offset;
	SLONG sectid;

	if (pSym->nType & SYMF_IMPORT) {
		/* Symbol should be imported */
		strcpy(symname, pSym->tzName);
		offset = 0;
		sectid = -1;
		type = SYM_IMPORT;
	} else if (pSym->nType & SYMF_EXPORT) {
		/* Symbol should be exported */
		strcpy(symname, pSym->tzName);
		type = SYM_EXPORT;
		offset = pSym->nValue;
		if (pSym->nType & SYMF_CONST)
			sectid = -1;
		else
			sectid = getsectid(pSym->pSection);
	} else {
		/* Symbol is local to this file */
		if (pSym->nType & SYMF_LOCAL) {
			strcpy(symname, pSym->pScope->tzName);
			strcat(symname, pSym->tzName);
		} else
			strcpy(symname, pSym->tzName);
		type = SYM_LOCAL;
		offset = pSym->nValue;
		sectid = getsectid(pSym->pSection);
	}

	fputstring(symname, f);
	fputc(type, f);

	if (type != SYM_IMPORT) {
		fputlong(sectid, f);
		fputlong(offset, f);
	}
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Add a symbol to the object
 *
 */

ULONG 
addsymbol(struct sSymbol * pSym)
{
	struct PatchSymbol *pPSym, **ppPSym;
	ULONG ID = 0;

	pPSym = pPatchSymbols;
	ppPSym = &(pPatchSymbols);

	while (pPSym) {
		if (pSym == pPSym->pSymbol)
			return (pPSym->ID);
		ppPSym = &(pPSym->pNext);
		pPSym = pPSym->pNext;
		ID += 1;
	}

	if ((*ppPSym = pPSym =
		(struct PatchSymbol *) malloc(sizeof(struct PatchSymbol))) !=
	    NULL) {
		pPSym->pNext = NULL;
		pPSym->pSymbol = pSym;
		pPSym->ID = ID;
		return (ID);
	} else
		fatalerror("No memory for patchsymbol");

	return ((ULONG) - 1);
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Add all exported symbols to the object
 *
 */

void 
addexports(void)
{
	int i;

	for (i = 0; i < HASHSIZE; i += 1) {
		struct sSymbol *pSym;

		pSym = tHashedSymbols[i];
		while (pSym) {
			if (pSym->nType & SYMF_EXPORT)
				addsymbol(pSym);
			pSym = pSym->pNext;
		}
	}
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Allocate a new patchstructure and link it into the list
 *
 */

struct Patch *
allocpatch(void)
{
	struct Patch *pPatch, **ppPatch;

	pPatch = pCurrentSection->pPatches;
	ppPatch = &(pCurrentSection->pPatches);

	while (pPatch) {
		ppPatch = &(pPatch->pNext);
		pPatch = pPatch->pNext;
	}

	if ((*ppPatch = pPatch =
		(struct Patch *) malloc(sizeof(struct Patch))) != NULL) {
		pPatch->pNext = NULL;
		pPatch->nRPNSize = 0;
		pPatch->pRPN = NULL;
	} else
		fatalerror("No memory for patch");

	return (pPatch);
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Create a new patch (includes the rpn expr)
 *
 */

void 
createpatch(ULONG type, struct Expression * expr)
{
	struct Patch *pPatch;
	UWORD rpndata;
	UBYTE rpnexpr[2048];
	char tzSym[512];
	ULONG rpnptr = 0, symptr;

	pPatch = allocpatch();
	pPatch->nType = type;
	strcpy(pPatch->tzFilename, tzCurrentFileName);
	pPatch->nLine = nLineNo;
	pPatch->nOffset = nPC;

	while ((rpndata = rpn_PopByte(expr)) != 0xDEAD) {
		switch (rpndata) {
		case RPN_CONST:
			rpnexpr[rpnptr++] = RPN_CONST;
			rpnexpr[rpnptr++] = rpn_PopByte(expr);
			rpnexpr[rpnptr++] = rpn_PopByte(expr);
			rpnexpr[rpnptr++] = rpn_PopByte(expr);
			rpnexpr[rpnptr++] = rpn_PopByte(expr);
			break;
		case RPN_SYM:
			symptr = 0;
			while ((tzSym[symptr++] = rpn_PopByte(expr)) != 0);
			if (sym_isConstant(tzSym)) {
				ULONG value;

				value = sym_GetConstantValue(tzSym);
				rpnexpr[rpnptr++] = RPN_CONST;
				rpnexpr[rpnptr++] = value & 0xFF;
				rpnexpr[rpnptr++] = value >> 8;
				rpnexpr[rpnptr++] = value >> 16;
				rpnexpr[rpnptr++] = value >> 24;
			} else {
				symptr = addsymbol(sym_FindSymbol(tzSym));
				rpnexpr[rpnptr++] = RPN_SYM;
				rpnexpr[rpnptr++] = symptr & 0xFF;
				rpnexpr[rpnptr++] = symptr >> 8;
				rpnexpr[rpnptr++] = symptr >> 16;
				rpnexpr[rpnptr++] = symptr >> 24;
			}
			break;
		case RPN_BANK:
			symptr = 0;
			while ((tzSym[symptr++] = rpn_PopByte(expr)) != 0);
			symptr = addsymbol(sym_FindSymbol(tzSym));
			rpnexpr[rpnptr++] = RPN_BANK;
			rpnexpr[rpnptr++] = symptr & 0xFF;
			rpnexpr[rpnptr++] = symptr >> 8;
			rpnexpr[rpnptr++] = symptr >> 16;
			rpnexpr[rpnptr++] = symptr >> 24;
			break;
		default:
			rpnexpr[rpnptr++] = rpndata;
			break;
		}
	}
	if ((pPatch->pRPN = (UBYTE *) malloc(rpnptr)) != NULL) {
		memcpy(pPatch->pRPN, rpnexpr, rpnptr);
		pPatch->nRPNSize = rpnptr;
	}
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * A quick check to see if we have an initialized section
 *
 */

void 
checksection(void)
{
	if (pCurrentSection)
		return;
	else
		fatalerror("Code generation before SECTION directive");
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * A quick check to see if we have an initialized section that can contain
 * this much initialized data
 *
 */

void 
checkcodesection(SLONG size)
{
	checksection();
	if ((pCurrentSection->nType == SECT_HOME
		|| pCurrentSection->nType == SECT_CODE)
	    && (pCurrentSection->nPC + size <= MAXSECTIONSIZE)) {
		if (((pCurrentSection->nPC % SECTIONCHUNK) >
			((pCurrentSection->nPC + size) % SECTIONCHUNK))
		    && (pCurrentSection->nType == SECT_HOME
			|| pCurrentSection->nType == SECT_CODE)) {
			if ((pCurrentSection->tData =
				(UBYTE *) realloc(pCurrentSection->tData,
				    ((pCurrentSection->nPC +
					    size) / SECTIONCHUNK +
					1) * SECTIONCHUNK)) != NULL) {
				return;
			} else
				fatalerror
				    ("Not enough memory to expand section");
		}
		return;
	} else
		fatalerror
		    ("Section can't contain initialized data or section limit exceeded");
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Write an objectfile
 *
 */

void 
out_WriteObject(void)
{
	FILE *f;

	addexports();

	if ((f = fopen(tzObjectname, "wb")) != NULL) {
		struct PatchSymbol *pSym;
		struct Section *pSect;

		fwrite("RGB2", 1, 4, f);
		fputlong(countsymbols(), f);
		fputlong(countsections(), f);

		pSym = pPatchSymbols;
		while (pSym) {
			writesymbol(pSym->pSymbol, f);
			pSym = pSym->pNext;
		}

		pSect = pSectionList;
		while (pSect) {
			writesection(pSect, f);
			pSect = pSect->pNext;
		}

		fclose(f);
	}
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Prepare for pass #2
 *
 */

void 
out_PrepPass2(void)
{
	struct Section *pSect;

	pSect = pSectionList;
	while (pSect) {
		pSect->nPC = 0;
		pSect = pSect->pNext;
	}
	pCurrentSection = NULL;
	pSectionStack = NULL;
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Set the objectfilename
 *
 */

void 
out_SetFileName(char *s)
{
	strcpy(tzObjectname, s);
	printf("Output filename %s\n", s);
	pSectionList = NULL;
	pCurrentSection = NULL;
	pPatchSymbols = NULL;
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Find a section by name and type.  If it doesn't exist, create it
 *
 */

struct Section *
out_FindSection(char *pzName, ULONG secttype, SLONG org,
    SLONG bank)
{
	struct Section *pSect, **ppSect;

	ppSect = &pSectionList;
	pSect = pSectionList;

	while (pSect) {
		if (strcmp(pzName, pSect->pzName) == 0) {
			if (secttype == pSect->nType
			    && ((ULONG) org) == pSect->nOrg
			    && ((ULONG) bank) == pSect->nBank) {
				return (pSect);
			} else
				fatalerror
				    ("Section already exists but with a different type");
		}
		ppSect = &(pSect->pNext);
		pSect = pSect->pNext;
	}

	if ((*ppSect =
		(pSect =
		    (struct Section *) malloc(sizeof(struct Section)))) != NULL) {
		if ((pSect->pzName =
			(char *) malloc(strlen(pzName) + 1)) != NULL) {
			strcpy(pSect->pzName, pzName);
			pSect->nType = secttype;
			pSect->nPC = 0;
			pSect->nOrg = org;
			pSect->nBank = bank;
			pSect->pNext = NULL;
			pSect->pPatches = NULL;
			pPatchSymbols = NULL;

			if ((pSect->tData =
				(UBYTE *) malloc(SECTIONCHUNK)) != NULL) {
				return (pSect);
			} else
				fatalerror("Not enough memory for section");
		} else
			fatalerror("Not enough memory for sectionname");
	} else
		fatalerror("Not enough memory for section");

	return (NULL);
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Set the current section
 *
 */

void 
out_SetCurrentSection(struct Section * pSect)
{
	pCurrentSection = pSect;
	nPC = pSect->nPC;

	pPCSymbol->nValue = nPC;
	pPCSymbol->pSection = pCurrentSection;
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Set the current section by name and type
 *
 */

void 
out_NewSection(char *pzName, ULONG secttype)
{
	out_SetCurrentSection(out_FindSection(pzName, secttype, -1, -1));
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Set the current section by name and type
 *
 */

void 
out_NewAbsSection(char *pzName, ULONG secttype, SLONG org, SLONG bank)
{
	out_SetCurrentSection(out_FindSection(pzName, secttype, org, bank));
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Output an absolute byte
 *
 */

void 
out_AbsByte(int b)
{
	checkcodesection(1);
	b &= 0xFF;
	if (nPass == 2)
		pCurrentSection->tData[nPC] = b;

	pCurrentSection->nPC += 1;
	nPC += 1;
	pPCSymbol->nValue += 1;
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Skip this many bytes
 *
 */

void 
out_Skip(int skip)
{
	checksection();
	if (!((pCurrentSection->nType == SECT_HOME)
		|| (pCurrentSection->nType == SECT_CODE))) {
		pCurrentSection->nPC += skip;
		nPC += skip;
		pPCSymbol->nValue += skip;
	} else {
		checkcodesection(skip);
		while (skip--)
			out_AbsByte(CurrentOptions.fillchar);
	}
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Output a NULL terminated string (excluding the NULL-character)
 *
 */

void 
out_String(char *s)
{
	checkcodesection(strlen(s));
	while (*s)
		out_AbsByte(*s++);
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Output a relocatable byte.  Checking will be done to see if it
 * is an absolute value in disguise.
 *
 */

void 
out_RelByte(struct Expression * expr)
{
	checkcodesection(1);
	if (rpn_isReloc(expr)) {
		if (nPass == 2) {
			pCurrentSection->tData[nPC] = 0;
			createpatch(PATCH_BYTE, expr);
		}
		pCurrentSection->nPC += 1;
		nPC += 1;
		pPCSymbol->nValue += 1;
	} else
		out_AbsByte(expr->nVal);

	rpn_Reset(expr);
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Output an absolute word
 *
 */

void 
out_AbsWord(int b)
{
	checkcodesection(2);
	b &= 0xFFFF;
	if (nPass == 2) {
		pCurrentSection->tData[nPC] = b & 0xFF;
		pCurrentSection->tData[nPC + 1] = b >> 8;
	}
	pCurrentSection->nPC += 2;
	nPC += 2;
	pPCSymbol->nValue += 2;
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Output a relocatable word.  Checking will be done to see if
 * is an absolute value in disguise.
 *
 */

void 
out_RelWord(struct Expression * expr)
{
	ULONG b;

	checkcodesection(2);
	b = expr->nVal & 0xFFFF;
	if (rpn_isReloc(expr)) {
		if (nPass == 2) {
			pCurrentSection->tData[nPC] = b & 0xFF;
			pCurrentSection->tData[nPC + 1] = b >> 8;
			createpatch(PATCH_WORD_L, expr);
		}
		pCurrentSection->nPC += 2;
		nPC += 2;
		pPCSymbol->nValue += 2;
	} else
		out_AbsWord(expr->nVal);
	rpn_Reset(expr);
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Output an absolute longword
 *
 */

void 
out_AbsLong(SLONG b)
{
	checkcodesection(sizeof(SLONG));
	if (nPass == 2) {
		pCurrentSection->tData[nPC] = b & 0xFF;
		pCurrentSection->tData[nPC + 1] = b >> 8;
		pCurrentSection->tData[nPC + 2] = b >> 16;
		pCurrentSection->tData[nPC + 3] = b >> 24;
	}
	pCurrentSection->nPC += 4;
	nPC += 4;
	pPCSymbol->nValue += 4;
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Output a relocatable longword.  Checking will be done to see if
 * is an absolute value in disguise.
 *
 */

void 
out_RelLong(struct Expression * expr)
{
	SLONG b;

	checkcodesection(4);
	b = expr->nVal;
	if (rpn_isReloc(expr)) {
		if (nPass == 2) {
			pCurrentSection->tData[nPC] = b & 0xFF;
			pCurrentSection->tData[nPC + 1] = b >> 8;
			pCurrentSection->tData[nPC + 2] = b >> 16;
			pCurrentSection->tData[nPC + 3] = b >> 24;
			createpatch(PATCH_LONG_L, expr);
		}
		pCurrentSection->nPC += 4;
		nPC += 4;
		pPCSymbol->nValue += 4;
	} else
		out_AbsLong(expr->nVal);
	rpn_Reset(expr);
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Output a PC-relative byte
 *
 */

void 
out_PCRelByte(struct Expression * expr)
{
	SLONG b = expr->nVal;

	checkcodesection(1);
	b = (b & 0xFFFF) - (nPC + 1);
	if (nPass == 2 && (b < -128 || b > 127))
		yyerror("PC-relative value must be 8-bit");

	out_AbsByte(b);
	rpn_Reset(expr);
}
/*
 * RGBAsm - OUTPUT.C - Outputs an objectfile
 *
 * Output a binary file
 *
 */

void 
out_BinaryFile(char *s)
{
	FILE *f;

	fstk_FindFile(s);

	if ((f = fopen(s, "rb")) != NULL) {
		SLONG fsize;

		fseek(f, 0, SEEK_END);
		fsize = ftell(f);
		fseek(f, 0, SEEK_SET);

		checkcodesection(fsize);

		if (nPass == 2) {
			SLONG dest = nPC;
			SLONG todo = fsize;

			while (todo--)
				pCurrentSection->tData[dest++] = fgetc(f);
		}
		pCurrentSection->nPC += fsize;
		nPC += fsize;
		pPCSymbol->nValue += fsize;
		fclose(f);
	} else
		fatalerror("File not found");
}

void 
out_BinaryFileSlice(char *s, SLONG start_pos, SLONG length)
{
	FILE *f;

	if (start_pos < 0)
		fatalerror("Start position cannot be negative");

	if (length < 0)
		fatalerror("Number of bytes to read must be greater than zero");

	fstk_FindFile(s);

	if ((f = fopen(s, "rb")) != NULL) {
		SLONG fsize;

		fseek(f, 0, SEEK_END);
		fsize = ftell(f);

		if (start_pos >= fsize)
			fatalerror("Specified start position is greater than length of file");

		if ((start_pos + length) > fsize)
			fatalerror("Specified range in INCBIN is out of bounds");

		fseek(f, start_pos, SEEK_SET);

		checkcodesection(length);

		if (nPass == 2) {
			SLONG dest = nPC;
			SLONG todo = length;

			while (todo--)
				pCurrentSection->tData[dest++] = fgetc(f);
		}
		pCurrentSection->nPC += length;
		nPC += length;
		pPCSymbol->nValue += length;

		fclose(f);
	} else
		fatalerror("File not found");
}