mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-23 11:32:07 +00:00
With permission from the main authors [1], most of the code has been relicensed under the MIT license. SPDX license identifiers are used so that the license headers in source code files aren't too large. Add CONTRIBUTORS.rst file. [1] https://github.com/rednex/rgbds/issues/128 Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
970 lines
19 KiB
C
970 lines
19 KiB
C
/*
|
|
* This file is part of RGBDS.
|
|
*
|
|
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
/*
|
|
* Outputs an objectfile
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "asm/asm.h"
|
|
#include "asm/charmap.h"
|
|
#include "asm/fstack.h"
|
|
#include "asm/main.h"
|
|
#include "asm/output.h"
|
|
#include "asm/rpn.h"
|
|
#include "asm/symbol.h"
|
|
|
|
#include "extern/err.h"
|
|
|
|
#include "common.h"
|
|
#include "linkdefs.h"
|
|
|
|
void out_SetCurrentSection(struct Section *pSect);
|
|
|
|
struct Patch {
|
|
char tzFilename[_MAX_PATH + 1];
|
|
uint32_t nLine;
|
|
uint32_t nOffset;
|
|
uint8_t nType;
|
|
uint32_t nRPNSize;
|
|
uint8_t *pRPN;
|
|
struct Patch *pNext;
|
|
};
|
|
|
|
struct PatchSymbol {
|
|
uint32_t ID;
|
|
struct sSymbol *pSymbol;
|
|
struct PatchSymbol *pNext;
|
|
struct PatchSymbol *pBucketNext; /* next symbol in hash table bucket */
|
|
};
|
|
|
|
struct SectionStackEntry {
|
|
struct Section *pSection;
|
|
struct sSymbol *pScope; /* Section's symbol scope */
|
|
struct SectionStackEntry *pNext;
|
|
};
|
|
|
|
struct PatchSymbol *tHashedPatchSymbols[HASHSIZE];
|
|
struct Section *pSectionList, *pCurrentSection;
|
|
struct PatchSymbol *pPatchSymbols;
|
|
struct PatchSymbol **ppPatchSymbolsTail = &pPatchSymbols;
|
|
char *tzObjectname;
|
|
struct SectionStackEntry *pSectionStack;
|
|
|
|
/*
|
|
* Section stack routines
|
|
*/
|
|
void out_PushSection(void)
|
|
{
|
|
struct SectionStackEntry *pSect;
|
|
|
|
pSect = malloc(sizeof(struct SectionStackEntry));
|
|
if (pSect == NULL)
|
|
fatalerror("No memory for section stack");
|
|
|
|
pSect->pSection = pCurrentSection;
|
|
pSect->pScope = sym_GetCurrentSymbolScope();
|
|
pSect->pNext = pSectionStack;
|
|
pSectionStack = pSect;
|
|
}
|
|
|
|
void out_PopSection(void)
|
|
{
|
|
if (pSectionStack == NULL)
|
|
fatalerror("No entries in the section stack");
|
|
|
|
struct SectionStackEntry *pSect;
|
|
|
|
pSect = pSectionStack;
|
|
out_SetCurrentSection(pSect->pSection);
|
|
sym_SetCurrentSymbolScope(pSect->pScope);
|
|
pSectionStack = pSect->pNext;
|
|
free(pSect);
|
|
}
|
|
|
|
static uint32_t getmaxsectionsize(uint32_t 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
|
|
*/
|
|
static uint32_t countsymbols(void)
|
|
{
|
|
struct PatchSymbol *pSym;
|
|
uint32_t count = 0;
|
|
|
|
pSym = pPatchSymbols;
|
|
|
|
while (pSym) {
|
|
count += 1;
|
|
pSym = pSym->pNext;
|
|
}
|
|
|
|
return (count);
|
|
}
|
|
|
|
/*
|
|
* Count the number of sections used in this object
|
|
*/
|
|
static uint32_t countsections(void)
|
|
{
|
|
struct Section *pSect;
|
|
uint32_t count = 0;
|
|
|
|
pSect = pSectionList;
|
|
|
|
while (pSect) {
|
|
count += 1;
|
|
pSect = pSect->pNext;
|
|
}
|
|
|
|
return (count);
|
|
}
|
|
|
|
/*
|
|
* Count the number of patches used in this object
|
|
*/
|
|
static uint32_t countpatches(struct Section *pSect)
|
|
{
|
|
struct Patch *pPatch;
|
|
uint32_t r = 0;
|
|
|
|
pPatch = pSect->pPatches;
|
|
while (pPatch) {
|
|
r += 1;
|
|
pPatch = pPatch->pNext;
|
|
}
|
|
|
|
return (r);
|
|
}
|
|
|
|
/*
|
|
* Write a long to a file (little-endian)
|
|
*/
|
|
static void fputlong(uint32_t 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
|
|
*/
|
|
static void fputstring(char *s, FILE *f)
|
|
{
|
|
while (*s)
|
|
fputc(*s++, f);
|
|
fputc(0, f);
|
|
}
|
|
|
|
/*
|
|
* Return a section's ID
|
|
*/
|
|
static uint32_t getsectid(struct Section *pSect)
|
|
{
|
|
struct Section *sec;
|
|
uint32_t ID = 0;
|
|
|
|
sec = pSectionList;
|
|
|
|
while (sec) {
|
|
if (sec == pSect)
|
|
return ID;
|
|
ID += 1;
|
|
sec = sec->pNext;
|
|
}
|
|
|
|
fatalerror("%s: Unknown section", __func__);
|
|
return (uint32_t)(-1);
|
|
}
|
|
|
|
/*
|
|
* Write a patch to a file
|
|
*/
|
|
static 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
|
|
*/
|
|
static 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
|
|
*/
|
|
static void writesymbol(struct sSymbol *pSym, FILE *f)
|
|
{
|
|
char symname[MAXSYMLEN * 2 + 1];
|
|
uint32_t type;
|
|
uint32_t offset;
|
|
int32_t 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
|
|
*/
|
|
static uint32_t nextID;
|
|
|
|
static uint32_t addsymbol(struct sSymbol *pSym)
|
|
{
|
|
struct PatchSymbol *pPSym, **ppPSym;
|
|
uint32_t hash;
|
|
|
|
hash = calchash(pSym->tzName);
|
|
ppPSym = &(tHashedPatchSymbols[hash]);
|
|
|
|
while ((*ppPSym) != NULL) {
|
|
if (pSym == (*ppPSym)->pSymbol)
|
|
return (*ppPSym)->ID;
|
|
ppPSym = &((*ppPSym)->pBucketNext);
|
|
}
|
|
|
|
pPSym = malloc(sizeof(struct PatchSymbol));
|
|
*ppPSym = pPSym;
|
|
|
|
if (pPSym == NULL)
|
|
fatalerror("No memory for patchsymbol");
|
|
|
|
pPSym->pNext = NULL;
|
|
pPSym->pBucketNext = NULL;
|
|
pPSym->pSymbol = pSym;
|
|
pPSym->ID = nextID++;
|
|
|
|
*ppPatchSymbolsTail = pPSym;
|
|
ppPatchSymbolsTail = &(pPSym->pNext);
|
|
|
|
return pPSym->ID;
|
|
}
|
|
|
|
/*
|
|
* Add all exported symbols to the object
|
|
*/
|
|
static void addexports(void)
|
|
{
|
|
int32_t 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;
|
|
|
|
pPatch = malloc(sizeof(struct Patch));
|
|
|
|
if (pPatch == NULL)
|
|
fatalerror("No memory for patch");
|
|
|
|
pPatch->pNext = pCurrentSection->pPatches;
|
|
pPatch->nRPNSize = 0;
|
|
pPatch->pRPN = NULL;
|
|
pCurrentSection->pPatches = pPatch;
|
|
|
|
return pPatch;
|
|
}
|
|
|
|
/*
|
|
* Create a new patch (includes the rpn expr)
|
|
*/
|
|
void createpatch(uint32_t type, struct Expression *expr)
|
|
{
|
|
struct Patch *pPatch;
|
|
uint16_t rpndata;
|
|
uint8_t rpnexpr[2048];
|
|
char tzSym[512];
|
|
uint32_t 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)) {
|
|
uint32_t 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 = sym_FindSymbol(tzSym);
|
|
|
|
if (sym == 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_SYM:
|
|
{
|
|
struct sSymbol *sym;
|
|
|
|
symptr = 0;
|
|
while ((tzSym[symptr++] = rpn_PopByte(expr)) != 0)
|
|
;
|
|
|
|
sym = sym_FindSymbol(tzSym);
|
|
if (sym == NULL)
|
|
break;
|
|
|
|
symptr = addsymbol(sym);
|
|
rpnexpr[rpnptr++] = RPN_BANK_SYM;
|
|
rpnexpr[rpnptr++] = symptr & 0xFF;
|
|
rpnexpr[rpnptr++] = symptr >> 8;
|
|
rpnexpr[rpnptr++] = symptr >> 16;
|
|
rpnexpr[rpnptr++] = symptr >> 24;
|
|
break;
|
|
}
|
|
case RPN_BANK_SECT:
|
|
{
|
|
uint16_t b;
|
|
|
|
rpnexpr[rpnptr++] = RPN_BANK_SECT;
|
|
|
|
do {
|
|
b = rpn_PopByte(expr);
|
|
rpnexpr[rpnptr++] = b & 0xFF;
|
|
} while (b != 0);
|
|
break;
|
|
}
|
|
default:
|
|
rpnexpr[rpnptr++] = rpndata;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pPatch->pRPN = malloc(rpnptr);
|
|
if (pPatch->pRPN != NULL) {
|
|
memcpy(pPatch->pRPN, rpnexpr, rpnptr);
|
|
pPatch->nRPNSize = rpnptr;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* A quick check to see if we have an initialized section
|
|
*/
|
|
static void checksection(void)
|
|
{
|
|
if (pCurrentSection == NULL)
|
|
fatalerror("Code generation before SECTION directive");
|
|
}
|
|
|
|
/*
|
|
* A quick check to see if we have an initialized section that can contain
|
|
* this much initialized data
|
|
*/
|
|
static 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.
|
|
*/
|
|
static void checksectionoverflow(uint32_t delta_size)
|
|
{
|
|
uint32_t 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 no path specified, don't write file */
|
|
if (tzObjectname == NULL)
|
|
return;
|
|
|
|
f = fopen(tzObjectname, "wb");
|
|
if (f == NULL)
|
|
fatalerror("Couldn't write file '%s'\n", tzObjectname);
|
|
|
|
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, uint32_t secttype, int32_t org,
|
|
int32_t bank, int32_t alignment)
|
|
{
|
|
struct Section *pSect, **ppSect;
|
|
|
|
ppSect = &pSectionList;
|
|
pSect = pSectionList;
|
|
|
|
while (pSect) {
|
|
if (strcmp(pzName, pSect->pzName) == 0) {
|
|
if (secttype == pSect->nType
|
|
&& ((uint32_t)org) == pSect->nOrg
|
|
&& ((uint32_t)bank) == pSect->nBank
|
|
&& ((uint32_t)alignment == pSect->nAlign)) {
|
|
return pSect;
|
|
}
|
|
|
|
fatalerror("Section already exists but with a different type");
|
|
}
|
|
ppSect = &(pSect->pNext);
|
|
pSect = pSect->pNext;
|
|
}
|
|
|
|
pSect = malloc(sizeof(struct Section));
|
|
*ppSect = pSect;
|
|
if (pSect == NULL)
|
|
fatalerror("Not enough memory for section");
|
|
|
|
pSect->pzName = malloc(strlen(pzName) + 1);
|
|
if (pSect->pzName == NULL)
|
|
fatalerror("Not enough memory for sectionname");
|
|
|
|
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;
|
|
|
|
/* It is only needed to allocate memory for ROM sections. */
|
|
if (secttype == SECT_ROM0 || secttype == SECT_ROMX) {
|
|
uint32_t sectsize;
|
|
|
|
sectsize = getmaxsectionsize(secttype, pzName);
|
|
pSect->tData = malloc(sectsize);
|
|
if (pSect->tData == NULL)
|
|
fatalerror("Not enough memory for section");
|
|
} else {
|
|
pSect->tData = NULL;
|
|
}
|
|
|
|
return (pSect);
|
|
}
|
|
|
|
/*
|
|
* 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, uint32_t secttype)
|
|
{
|
|
out_SetCurrentSection(out_FindSection(pzName, secttype, -1, -1, 1));
|
|
}
|
|
|
|
/*
|
|
* Set the current section by name and type
|
|
*/
|
|
void out_NewAbsSection(char *pzName, uint32_t secttype, int32_t org,
|
|
int32_t 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, uint32_t secttype, int32_t alignment,
|
|
int32_t 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(int32_t 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(int32_t b)
|
|
{
|
|
checkcodesection();
|
|
out_AbsByteBypassCheck(b);
|
|
}
|
|
|
|
void out_AbsByteGroup(char *s, int32_t length)
|
|
{
|
|
checkcodesection();
|
|
checksectionoverflow(length);
|
|
while (length--)
|
|
out_AbsByte(*s++);
|
|
}
|
|
|
|
/*
|
|
* Skip this many bytes
|
|
*/
|
|
void out_Skip(int32_t 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(int32_t 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)
|
|
{
|
|
uint32_t 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(int32_t b)
|
|
{
|
|
checkcodesection();
|
|
checksectionoverflow(sizeof(int32_t));
|
|
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)
|
|
{
|
|
int32_t 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)
|
|
{
|
|
int32_t 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);
|
|
|
|
int32_t fsize;
|
|
|
|
fseek(f, 0, SEEK_END);
|
|
fsize = ftell(f);
|
|
fseek(f, 0, SEEK_SET);
|
|
|
|
checkcodesection();
|
|
checksectionoverflow(fsize);
|
|
|
|
if (nPass == 2) {
|
|
int32_t dest = nPC;
|
|
int32_t todo = fsize;
|
|
|
|
while (todo--)
|
|
pCurrentSection->tData[dest++] = fgetc(f);
|
|
}
|
|
pCurrentSection->nPC += fsize;
|
|
nPC += fsize;
|
|
pPCSymbol->nValue += fsize;
|
|
fclose(f);
|
|
}
|
|
|
|
void out_BinaryFileSlice(char *s, int32_t start_pos, int32_t 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);
|
|
|
|
int32_t 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) {
|
|
int32_t dest = nPC;
|
|
int32_t todo = length;
|
|
|
|
while (todo--)
|
|
pCurrentSection->tData[dest++] = fgetc(f);
|
|
}
|
|
pCurrentSection->nPC += length;
|
|
nPC += length;
|
|
pPCSymbol->nValue += length;
|
|
|
|
fclose(f);
|
|
}
|