mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
948 lines
18 KiB
C
948 lines
18 KiB
C
/*
|
|
* Outputs an objectfile
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "asm/asm.h"
|
|
#include "asm/charmap.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"
|
|
#include "common.h"
|
|
#include "extern/err.h"
|
|
|
|
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 PatchSymbol *pBucketNext; // next symbol in hash table bucket
|
|
};
|
|
|
|
struct SectionStackEntry {
|
|
struct Section *pSection;
|
|
struct SectionStackEntry *pNext;
|
|
};
|
|
|
|
struct PatchSymbol *tHashedPatchSymbols[HASHSIZE];
|
|
struct Section *pSectionList = NULL, *pCurrentSection = NULL;
|
|
struct PatchSymbol *pPatchSymbols = NULL;
|
|
struct PatchSymbol **ppPatchSymbolsTail = &pPatchSymbols;
|
|
char *tzObjectname;
|
|
struct SectionStackEntry *pSectionStack = NULL;
|
|
|
|
/*
|
|
* Section stack routines
|
|
*/
|
|
void
|
|
out_PushSection(void)
|
|
{
|
|
struct SectionStackEntry *pSect;
|
|
|
|
if ((pSect = 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");
|
|
}
|
|
|
|
ULONG
|
|
getmaxsectionsize(ULONG secttype, char * sectname)
|
|
{
|
|
switch (secttype)
|
|
{
|
|
case SECT_ROM0: return 0x8000; /* If ROMX sections not used. */
|
|
case SECT_ROMX: return 0x4000;
|
|
case SECT_VRAM: return 0x2000;
|
|
case SECT_SRAM: return 0x2000;
|
|
case SECT_WRAM0: return 0x2000; /* If WRAMX sections not used. */
|
|
case SECT_WRAMX: return 0x1000;
|
|
case SECT_OAM: return 0xA0;
|
|
case SECT_HRAM: return 0x7F;
|
|
default: break;
|
|
}
|
|
errx(1, "Section \"%s\" has an invalid section type.", sectname);
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
/*
|
|
* Write a NULL-terminated string to a file
|
|
*/
|
|
void
|
|
fputstring(char *s, FILE * f)
|
|
{
|
|
while (*s)
|
|
fputc(*s++, f);
|
|
fputc(0, f);
|
|
}
|
|
|
|
/*
|
|
* Return a section's 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);
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
/*
|
|
* Write a section to a file
|
|
*/
|
|
void
|
|
writesection(struct Section * pSect, FILE * f)
|
|
{
|
|
fputstring(pSect->pzName, f);
|
|
|
|
fputlong(pSect->nPC, f);
|
|
|
|
fputc(pSect->nType, f);
|
|
|
|
fputlong(pSect->nOrg, f);
|
|
fputlong(pSect->nBank, f);
|
|
fputlong(pSect->nAlign, f);
|
|
|
|
if ((pSect->nType == SECT_ROM0)
|
|
|| (pSect->nType == SECT_ROMX)) {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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 {
|
|
strcpy(symname, pSym->tzName);
|
|
|
|
if (pSym->nType & SYMF_EXPORT) {
|
|
/* Symbol should be exported */
|
|
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 */
|
|
type = SYM_LOCAL;
|
|
offset = pSym->nValue;
|
|
sectid = getsectid(pSym->pSection);
|
|
}
|
|
}
|
|
|
|
fputstring(symname, f);
|
|
fputc(type, f);
|
|
|
|
if (type != SYM_IMPORT) {
|
|
fputstring(pSym->tzFileName, f);
|
|
fputlong(pSym->nFileLine, f);
|
|
|
|
fputlong(sectid, f);
|
|
fputlong(offset, f);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Add a symbol to the object
|
|
*/
|
|
ULONG
|
|
addsymbol(struct sSymbol * pSym)
|
|
{
|
|
struct PatchSymbol *pPSym, **ppPSym;
|
|
static ULONG nextID = 0;
|
|
ULONG hash;
|
|
|
|
hash = calchash(pSym->tzName);
|
|
ppPSym = &(tHashedPatchSymbols[hash]);
|
|
|
|
while ((*ppPSym) != NULL) {
|
|
if (pSym == (*ppPSym)->pSymbol)
|
|
return (*ppPSym)->ID;
|
|
ppPSym = &((*ppPSym)->pBucketNext);
|
|
}
|
|
|
|
if ((*ppPSym = pPSym = malloc(sizeof(struct PatchSymbol))) != NULL) {
|
|
pPSym->pNext = NULL;
|
|
pPSym->pBucketNext = NULL;
|
|
pPSym->pSymbol = pSym;
|
|
pPSym->ID = nextID++;
|
|
} else
|
|
fatalerror("No memory for patchsymbol");
|
|
|
|
*ppPatchSymbolsTail = pPSym;
|
|
ppPatchSymbolsTail = &(pPSym->pNext);
|
|
|
|
return pPSym->ID;
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Allocate a new patchstructure and link it into the list
|
|
*/
|
|
struct Patch *
|
|
allocpatch(void)
|
|
{
|
|
struct Patch *pPatch;
|
|
|
|
if ((pPatch = malloc(sizeof(struct Patch))) != NULL) {
|
|
pPatch->pNext = pCurrentSection->pPatches;
|
|
pPatch->nRPNSize = 0;
|
|
pPatch->pRPN = NULL;
|
|
} else
|
|
fatalerror("No memory for patch");
|
|
|
|
pCurrentSection->pPatches = pPatch;
|
|
|
|
return (pPatch);
|
|
}
|
|
|
|
/*
|
|
* 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 {
|
|
struct sSymbol *sym;
|
|
if ((sym = sym_FindSymbol(tzSym)) == NULL)
|
|
break;
|
|
symptr = addsymbol(sym);
|
|
rpnexpr[rpnptr++] = RPN_SYM;
|
|
rpnexpr[rpnptr++] = symptr & 0xFF;
|
|
rpnexpr[rpnptr++] = symptr >> 8;
|
|
rpnexpr[rpnptr++] = symptr >> 16;
|
|
rpnexpr[rpnptr++] = symptr >> 24;
|
|
}
|
|
break;
|
|
case RPN_BANK: {
|
|
struct sSymbol *sym;
|
|
symptr = 0;
|
|
while ((tzSym[symptr++] = rpn_PopByte(expr)) != 0);
|
|
if ((sym = sym_FindSymbol(tzSym)) == NULL)
|
|
break;
|
|
symptr = addsymbol(sym);
|
|
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 = malloc(rpnptr)) != NULL) {
|
|
memcpy(pPatch->pRPN, rpnexpr, rpnptr);
|
|
pPatch->nRPNSize = rpnptr;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* A quick check to see if we have an initialized section
|
|
*/
|
|
void
|
|
checksection(void)
|
|
{
|
|
if (pCurrentSection)
|
|
return;
|
|
else
|
|
fatalerror("Code generation before SECTION directive");
|
|
}
|
|
|
|
/*
|
|
* A quick check to see if we have an initialized section that can contain
|
|
* this much initialized data
|
|
*/
|
|
void
|
|
checkcodesection(void)
|
|
{
|
|
checksection();
|
|
if (pCurrentSection->nType != SECT_ROM0 &&
|
|
pCurrentSection->nType != SECT_ROMX) {
|
|
fatalerror("Section '%s' cannot contain code or data (not ROM0 or ROMX)",
|
|
pCurrentSection->pzName);
|
|
} else if (nUnionDepth > 0) {
|
|
fatalerror("UNIONs cannot contain code or data");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check if the section has grown too much.
|
|
*/
|
|
void
|
|
checksectionoverflow(ULONG delta_size)
|
|
{
|
|
ULONG maxsize = getmaxsectionsize(pCurrentSection->nType,
|
|
pCurrentSection->pzName);
|
|
|
|
if (pCurrentSection->nPC + delta_size > maxsize) {
|
|
/*
|
|
* This check is here to trap broken code that generates
|
|
* sections that are too big and to prevent the assembler from
|
|
* generating huge object files or trying to allocate too much
|
|
* memory.
|
|
* The real check must be done at the linking stage.
|
|
*/
|
|
fatalerror("Section '%s' is too big (max size = 0x%X bytes).",
|
|
pCurrentSection->pzName, maxsize);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Write an objectfile
|
|
*/
|
|
void
|
|
out_WriteObject(void)
|
|
{
|
|
FILE *f;
|
|
|
|
addexports();
|
|
|
|
if ((f = fopen(tzObjectname, "wb")) != NULL) {
|
|
struct PatchSymbol *pSym;
|
|
struct Section *pSect;
|
|
|
|
fwrite(RGBDS_OBJECT_VERSION_STRING, 1,
|
|
strlen(RGBDS_OBJECT_VERSION_STRING), 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);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
/*
|
|
* Set the objectfilename
|
|
*/
|
|
void
|
|
out_SetFileName(char *s)
|
|
{
|
|
tzObjectname = s;
|
|
if (CurrentOptions.verbose) {
|
|
printf("Output filename %s\n", s);
|
|
}
|
|
pSectionList = NULL;
|
|
pCurrentSection = NULL;
|
|
pPatchSymbols = NULL;
|
|
}
|
|
|
|
/*
|
|
* 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, SLONG alignment)
|
|
{
|
|
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
|
|
&& ((ULONG) alignment == pSect->nAlign)) {
|
|
return (pSect);
|
|
} else
|
|
fatalerror
|
|
("Section already exists but with a different type");
|
|
}
|
|
ppSect = &(pSect->pNext);
|
|
pSect = pSect->pNext;
|
|
}
|
|
|
|
if ((*ppSect = (pSect = malloc(sizeof(struct Section)))) != NULL) {
|
|
if ((pSect->pzName = malloc(strlen(pzName) + 1)) != NULL) {
|
|
strcpy(pSect->pzName, pzName);
|
|
pSect->nType = secttype;
|
|
pSect->nPC = 0;
|
|
pSect->nOrg = org;
|
|
pSect->nBank = bank;
|
|
pSect->nAlign = alignment;
|
|
pSect->pNext = NULL;
|
|
pSect->pPatches = NULL;
|
|
pSect->charmap = NULL;
|
|
pPatchSymbols = NULL;
|
|
|
|
pSect->tData = NULL;
|
|
if (secttype == SECT_ROM0 || secttype == SECT_ROMX) {
|
|
/* It is only needed to allocate memory for ROM
|
|
* sections. */
|
|
ULONG sectsize = getmaxsectionsize(secttype, pzName);
|
|
if ((pSect->tData = malloc(sectsize)) == NULL)
|
|
fatalerror("Not enough memory for section");
|
|
}
|
|
return (pSect);
|
|
} else
|
|
fatalerror("Not enough memory for sectionname");
|
|
} else
|
|
fatalerror("Not enough memory for section");
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* Set the current section
|
|
*/
|
|
void
|
|
out_SetCurrentSection(struct Section * pSect)
|
|
{
|
|
if (nUnionDepth > 0) {
|
|
fatalerror("Cannot change the section within a UNION");
|
|
}
|
|
|
|
pCurrentSection = pSect;
|
|
nPC = pSect->nPC;
|
|
|
|
pPCSymbol->nValue = nPC;
|
|
pPCSymbol->pSection = pCurrentSection;
|
|
}
|
|
|
|
/*
|
|
* Set the current section by name and type
|
|
*/
|
|
void
|
|
out_NewSection(char *pzName, ULONG secttype)
|
|
{
|
|
out_SetCurrentSection(out_FindSection(pzName, secttype, -1, -1, 1));
|
|
}
|
|
|
|
/*
|
|
* 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, 1));
|
|
}
|
|
|
|
/*
|
|
* Set the current section by name and type, using a given byte alignment
|
|
*/
|
|
void
|
|
out_NewAlignedSection(char *pzName, ULONG secttype, SLONG alignment, SLONG bank)
|
|
{
|
|
if (alignment < 0 || alignment > 16) {
|
|
yyerror("Alignment must be between 0-16 bits.");
|
|
}
|
|
out_SetCurrentSection(out_FindSection(pzName, secttype, -1, bank, 1 << alignment));
|
|
}
|
|
|
|
/*
|
|
* Output an absolute byte (bypassing ROM/union checks)
|
|
*/
|
|
void
|
|
out_AbsByteBypassCheck(int b)
|
|
{
|
|
checksectionoverflow(1);
|
|
b &= 0xFF;
|
|
if (nPass == 2)
|
|
pCurrentSection->tData[nPC] = b;
|
|
|
|
pCurrentSection->nPC += 1;
|
|
nPC += 1;
|
|
pPCSymbol->nValue += 1;
|
|
}
|
|
|
|
/*
|
|
* Output an absolute byte
|
|
*/
|
|
void
|
|
out_AbsByte(int b)
|
|
{
|
|
checkcodesection();
|
|
out_AbsByteBypassCheck(b);
|
|
}
|
|
|
|
void
|
|
out_AbsByteGroup(char *s, int length)
|
|
{
|
|
checkcodesection();
|
|
checksectionoverflow(length);
|
|
while (length--)
|
|
out_AbsByte(*s++);
|
|
}
|
|
|
|
/*
|
|
* Skip this many bytes
|
|
*/
|
|
void
|
|
out_Skip(int skip)
|
|
{
|
|
checksection();
|
|
checksectionoverflow(skip);
|
|
if (!((pCurrentSection->nType == SECT_ROM0)
|
|
|| (pCurrentSection->nType == SECT_ROMX))) {
|
|
pCurrentSection->nPC += skip;
|
|
nPC += skip;
|
|
pPCSymbol->nValue += skip;
|
|
} else if (nUnionDepth > 0) {
|
|
while (skip--)
|
|
out_AbsByteBypassCheck(CurrentOptions.fillchar);
|
|
} else {
|
|
checkcodesection();
|
|
while (skip--)
|
|
out_AbsByte(CurrentOptions.fillchar);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Output a NULL terminated string (excluding the NULL-character)
|
|
*/
|
|
void
|
|
out_String(char *s)
|
|
{
|
|
checkcodesection();
|
|
checksectionoverflow(strlen(s));
|
|
while (*s)
|
|
out_AbsByte(*s++);
|
|
}
|
|
|
|
/*
|
|
* 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();
|
|
checksectionoverflow(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);
|
|
}
|
|
|
|
/*
|
|
* Output an absolute word
|
|
*/
|
|
void
|
|
out_AbsWord(int b)
|
|
{
|
|
checkcodesection();
|
|
checksectionoverflow(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;
|
|
}
|
|
|
|
/*
|
|
* Output a relocatable word. Checking will be done to see if
|
|
* it's an absolute value in disguise.
|
|
*/
|
|
void
|
|
out_RelWord(struct Expression * expr)
|
|
{
|
|
ULONG b;
|
|
|
|
checkcodesection();
|
|
checksectionoverflow(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);
|
|
}
|
|
|
|
/*
|
|
* Output an absolute longword
|
|
*/
|
|
void
|
|
out_AbsLong(SLONG b)
|
|
{
|
|
checkcodesection();
|
|
checksectionoverflow(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;
|
|
}
|
|
|
|
/*
|
|
* 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();
|
|
checksectionoverflow(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);
|
|
}
|
|
|
|
/*
|
|
* Output a PC-relative byte
|
|
*/
|
|
void
|
|
out_PCRelByte(struct Expression * expr)
|
|
{
|
|
SLONG b = expr->nVal;
|
|
|
|
checkcodesection();
|
|
checksectionoverflow(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);
|
|
}
|
|
|
|
/*
|
|
* Output a binary file
|
|
*/
|
|
void
|
|
out_BinaryFile(char *s)
|
|
{
|
|
FILE *f;
|
|
|
|
f = fstk_FindFile(s);
|
|
if (f == NULL) {
|
|
err(1, "Unable to open incbin file '%s'", s);
|
|
}
|
|
|
|
SLONG fsize;
|
|
|
|
fseek(f, 0, SEEK_END);
|
|
fsize = ftell(f);
|
|
fseek(f, 0, SEEK_SET);
|
|
|
|
checkcodesection();
|
|
checksectionoverflow(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);
|
|
}
|
|
|
|
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");
|
|
|
|
f = fstk_FindFile(s);
|
|
if (f == NULL) {
|
|
err(1, "Unable to open included file '%s'", s);
|
|
}
|
|
|
|
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();
|
|
checksectionoverflow(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);
|
|
}
|