diff --git a/Makefile b/Makefile index 96b08cd8..e7927fdc 100644 --- a/Makefile +++ b/Makefile @@ -61,6 +61,7 @@ rgbasm_obj := \ src/asm/math.o \ src/asm/output.o \ src/asm/rpn.o \ + src/asm/section.o \ src/asm/symbol.o \ src/asm/util.o \ src/asm/warning.o \ diff --git a/include/asm/fstack.h b/include/asm/fstack.h index b71bd718..961583aa 100644 --- a/include/asm/fstack.h +++ b/include/asm/fstack.h @@ -48,7 +48,7 @@ void fstk_DumpStringExpansions(void); void fstk_AddIncludePath(char *s); uint32_t fstk_RunMacro(char *s); void fstk_RunRept(uint32_t count, int32_t nReptLineNo); -FILE *fstk_FindFile(char *fname, char **incPathUsed); +FILE *fstk_FindFile(char const *fname, char **incPathUsed); int32_t fstk_GetLine(void); #endif /* RGBDS_ASM_FSTACK_H */ diff --git a/include/asm/output.h b/include/asm/output.h index f888f6bd..03ef4d09 100644 --- a/include/asm/output.h +++ b/include/asm/output.h @@ -11,43 +11,15 @@ #include -#include "asm/rpn.h" +#include "linkdefs.h" -struct Section { - char *pzName; - uint8_t nType; - uint32_t nPC; - uint32_t nOrg; - uint32_t nBank; - uint32_t nAlign; - struct Section *pNext; - struct Patch *pPatches; - uint8_t *tData; -}; +struct Expression; extern char *tzObjectname; +extern struct Section *pSectionList, *pCurrentSection; -void out_PrepPass2(void); void out_SetFileName(char *s); -struct Section *out_FindSectionByName(const char *pzName); -void out_NewSection(char *pzName, uint32_t secttype); -void out_NewAbsSection(char *pzName, uint32_t secttype, int32_t org, - int32_t bank); -void out_NewAlignedSection(char *pzName, uint32_t secttype, int32_t alignment, - int32_t bank); -void out_AbsByte(int32_t b); -void out_AbsByteGroup(char *s, int32_t length); -void out_RelByte(struct Expression *expr); -void out_RelWord(struct Expression *expr); -void out_PCRelByte(struct Expression *expr); +void out_CreatePatch(uint32_t type, struct Expression *expr); void out_WriteObject(void); -void out_Skip(int32_t skip); -void out_BinaryFile(char *s); -void out_BinaryFileSlice(char *s, int32_t start_pos, int32_t length); -void out_String(char *s); -void out_AbsLong(int32_t b); -void out_RelLong(struct Expression *expr); -void out_PushSection(void); -void out_PopSection(void); #endif /* RGBDS_ASM_OUTPUT_H */ diff --git a/include/asm/section.h b/include/asm/section.h new file mode 100644 index 00000000..9e006ca6 --- /dev/null +++ b/include/asm/section.h @@ -0,0 +1,46 @@ + +#include + +#include "linkdefs.h" + +struct Expression; + +struct Section { + char *pzName; + enum SectionType nType; + uint32_t nPC; + uint32_t nOrg; + uint32_t nBank; + uint32_t nAlign; + struct Section *pNext; + struct Patch *pPatches; + uint8_t *tData; +}; + +struct SectionSpec { + int32_t bank; + int32_t alignment; +}; + +struct Section *out_FindSectionByName(const char *pzName); +void out_NewSection(char const *pzName, uint32_t secttype, int32_t org, + struct SectionSpec const *attributes); +void out_SetLoadSection(char const *name, uint32_t secttype, int32_t org, + struct SectionSpec const *attributes); +void out_EndLoadSection(void); + +struct Section *sect_GetSymbolSection(void); + +void out_AbsByte(uint8_t b); +void out_AbsByteGroup(uint8_t const *s, int32_t length); +void out_Skip(int32_t skip); +void out_String(char const *s); +void out_RelByte(struct Expression *expr); +void out_RelWord(struct Expression *expr); +void out_RelLong(struct Expression *expr); +void out_PCRelByte(struct Expression *expr); +void out_BinaryFile(char const *s); +void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length); + +void out_PushSection(void); +void out_PopSection(void); diff --git a/src/asm/asmy.y b/src/asm/asmy.y index 4406ead6..9df5a5ed 100644 --- a/src/asm/asmy.y +++ b/src/asm/asmy.y @@ -22,8 +22,8 @@ #include "asm/lexer.h" #include "asm/main.h" #include "asm/mymath.h" -#include "asm/output.h" #include "asm/rpn.h" +#include "asm/section.h" #include "asm/symbol.h" #include "asm/util.h" #include "asm/warning.h" @@ -37,20 +37,6 @@ char *tzNewMacro; uint32_t ulNewMacroSize; int32_t nPCOffset; -static void bankrangecheck(char *name, uint32_t secttype, int32_t org, - int32_t bank) -{ - if (secttype != SECTTYPE_ROMX && secttype != SECTTYPE_VRAM - && secttype != SECTTYPE_SRAM && secttype != SECTTYPE_WRAMX) - yyerror("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections"); - else if (bank < bankranges[secttype][0] || bank > bankranges[secttype][1]) - yyerror("%s bank value $%x out of range ($%x to $%x)", - typeNames[secttype], bank, bankranges[secttype][0], - bankranges[secttype][1]); - - out_NewAbsSection(name, secttype, org, bank); -} - size_t symvaluetostring(char *dest, size_t maxLength, char *symName, const char *mode) { @@ -419,7 +405,6 @@ static void updateUnion(void) nPC = unionStart[unionIndex]; pCurrentSection->nPC = unionStart[unionIndex]; - pPCSymbol->nValue = unionStart[unionIndex]; } static size_t strlenUTF8(const char *s) @@ -509,6 +494,7 @@ static void strsubUTF8(char *dest, const char *src, uint32_t pos, uint32_t len) char tzString[MAXSTRLEN + 1]; struct Expression sVal; int32_t nConstValue; + struct SectionSpec sectSpec; } %type relocconst @@ -521,6 +507,9 @@ static void strsubUTF8(char *dest, const char *src, uint32_t pos, uint32_t len) %type string +%type sectorg +%type sectattrs + %token T_NUMBER %token T_STRING @@ -586,6 +575,7 @@ static void strsubUTF8(char *dest, const char *src, uint32_t pos, uint32_t len) %token T_POP_POPC %token T_POP_SHIFT %token T_POP_ENDR +%token T_POP_LOAD T_POP_ENDL %token T_POP_FAIL %token T_POP_WARN %token T_POP_PURGE @@ -741,6 +731,7 @@ simple_pseudoop : include | setcharmap | pushc | popc + | load | rept | shift | fail @@ -788,6 +779,15 @@ warn : T_POP_WARN string { warning(WARNING_USER, "%s", $2); } shift : T_POP_SHIFT { sym_ShiftCurrentMacroArgs(); } ; +load : T_POP_LOAD string comma sectiontype sectorg sectattrs + { + out_SetLoadSection($2, $4, $5, &$6); + } + | T_POP_ENDL + { + out_EndLoadSection(); + } + rept : T_POP_REPT uconst { uint32_t nDefinitionLineNo = nLineNo; @@ -868,7 +868,6 @@ endu : T_POP_ENDU nUnionDepth--; nPC = unionStart[nUnionDepth] + unionSize[nUnionDepth]; pCurrentSection->nPC = nPC; - pPCSymbol->nValue = nPC; } ; @@ -1149,7 +1148,7 @@ constlist_8bit_entry : /* empty */ char *s = $1; int32_t length = charmap_Convert(&s); - out_AbsByteGroup(s, length); + out_AbsByteGroup((uint8_t*)s, length); free(s); } ; @@ -1384,38 +1383,9 @@ string : T_STRING } ; -section : T_POP_SECTION string comma sectiontype +section : T_POP_SECTION string comma sectiontype sectorg sectattrs { - out_NewSection($2, $4); - } - | T_POP_SECTION string comma sectiontype '[' uconst ']' - { - if (($6 >= 0) && ($6 < 0x10000)) - out_NewAbsSection($2, $4, $6, -1); - else - yyerror("Address $%x not 16-bit", $6); - } - | T_POP_SECTION string comma sectiontype comma T_OP_ALIGN '[' uconst ']' - { - out_NewAlignedSection($2, $4, $8, -1); - } - | T_POP_SECTION string comma sectiontype comma T_OP_BANK '[' uconst ']' - { - bankrangecheck($2, $4, -1, $8); - } - | T_POP_SECTION string comma sectiontype '[' uconst ']' comma T_OP_BANK '[' uconst ']' - { - if (($6 < 0) || ($6 > 0x10000)) - yyerror("Address $%x not 16-bit", $6); - bankrangecheck($2, $4, $6, $11); - } - | T_POP_SECTION string comma sectiontype comma T_OP_ALIGN '[' uconst ']' comma T_OP_BANK '[' uconst ']' - { - out_NewAlignedSection($2, $4, $8, $13); - } - | T_POP_SECTION string comma sectiontype comma T_OP_BANK '[' uconst ']' comma T_OP_ALIGN '[' uconst ']' - { - out_NewAlignedSection($2, $4, $13, $8); + out_NewSection($2, $4, $5, &$6); } ; @@ -1449,6 +1419,36 @@ sectiontype : T_SECT_WRAM0 { $$ = SECTTYPE_WRAM0; } } ; +sectorg : { $$ = -1; } + | '[' uconst ']' + { + if ($2 < 0 || $2 >= 0x10000) + yyerror("Address $%x is not 16-bit", $2); + else + $$ = $2; + } +; + +sectattrs : + { + $$.alignment = 0; + $$.bank = -1; + } + | sectattrs comma T_OP_ALIGN '[' uconst ']' + { + if ($5 < 0 || $5 > 16) + yyerror("Alignment must be between 0 and 16 bits, not %u", + $5); + else + $$.alignment = $5; + } + | sectattrs comma T_OP_BANK '[' uconst ']' + { + /* We cannot check the validity of this now */ + $$.bank = $5; + } +; + cpu_command : z80_adc | z80_add diff --git a/src/asm/fstack.c b/src/asm/fstack.c index 4624b377..7ee2d329 100644 --- a/src/asm/fstack.c +++ b/src/asm/fstack.c @@ -334,7 +334,7 @@ static void printdep(const char *fileName) } } -FILE *fstk_FindFile(char *fname, char **incPathUsed) +FILE *fstk_FindFile(char const *fname, char **incPathUsed) { char path[_MAX_PATH]; int32_t i; diff --git a/src/asm/globlex.c b/src/asm/globlex.c index 44881a96..6ce08032 100644 --- a/src/asm/globlex.c +++ b/src/asm/globlex.c @@ -17,7 +17,7 @@ #include "asm/lexer.h" #include "asm/main.h" #include "asm/rpn.h" -#include "asm/symbol.h" +#include "asm/section.h" #include "asm/symbol.h" #include "asm/warning.h" @@ -508,6 +508,9 @@ const struct sLexInitString lexer_strings[] = { /* Not needed but we have it here just to protect the name */ {"endr", T_POP_ENDR}, + {"load", T_POP_LOAD}, + {"endl", T_POP_ENDL}, + {"if", T_POP_IF}, {"else", T_POP_ELSE}, {"elif", T_POP_ELIF}, diff --git a/src/asm/lexer.c b/src/asm/lexer.c index 23e00e0c..69f3be1e 100644 --- a/src/asm/lexer.c +++ b/src/asm/lexer.c @@ -19,6 +19,7 @@ #include "asm/lexer.h" #include "asm/main.h" #include "asm/rpn.h" +#include "asm/section.h" #include "asm/warning.h" #include "extern/err.h" diff --git a/src/asm/output.c b/src/asm/output.c index d8a2234b..77bcd53a 100644 --- a/src/asm/output.c +++ b/src/asm/output.c @@ -23,6 +23,7 @@ #include "asm/main.h" #include "asm/output.h" #include "asm/rpn.h" +#include "asm/section.h" #include "asm/symbol.h" #include "asm/warning.h" @@ -30,8 +31,6 @@ #include "linkdefs.h" -void out_SetCurrentSection(struct Section *pSect); - struct Patch { char tzFilename[_MAX_PATH + 1]; uint32_t nLine; @@ -49,74 +48,11 @@ struct PatchSymbol { 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 SECTTYPE_ROM0: - return 0x8000; /* If ROMX sections not used */ - case SECTTYPE_ROMX: - return 0x4000; - case SECTTYPE_VRAM: - return 0x2000; - case SECTTYPE_SRAM: - return 0x2000; - case SECTTYPE_WRAM0: - return 0x2000; /* If WRAMX sections not used */ - case SECTTYPE_WRAMX: - return 0x1000; - case SECTTYPE_OAM: - return 0xA0; - case SECTTYPE_HRAM: - return 0x7F; - default: - break; - } - errx(1, "Section \"%s\" has an invalid section type.", sectname); -} /* * Count the number of symbols used in this object @@ -127,13 +63,12 @@ static uint32_t countsymbols(void) uint32_t count = 0; pSym = pPatchSymbols; - while (pSym) { count++; pSym = pSym->pNext; } - return (count); + return count; } /* @@ -145,13 +80,12 @@ static uint32_t countsections(void) uint32_t count = 0; pSect = pSectionList; - while (pSect) { count++; pSect = pSect->pNext; } - return (count); + return count; } /* @@ -168,7 +102,7 @@ static uint32_t countpatches(struct Section *pSect) pPatch = pPatch->pNext; } - return (r); + return r; } /* @@ -209,8 +143,7 @@ static uint32_t getsectid(struct Section *pSect) sec = sec->pNext; } - fatalerror("%s: Unknown section", __func__); - return (uint32_t)(-1); + fatalerror("Unknown section '%s'", pSect->pzName); } /* @@ -379,7 +312,7 @@ struct Patch *allocpatch(void) /* * Create a new patch (includes the rpn expr) */ -void createpatch(uint32_t type, struct Expression *expr) +void out_CreatePatch(uint32_t type, struct Expression *expr) { struct Patch *pPatch; uint16_t rpndata; @@ -396,7 +329,7 @@ void createpatch(uint32_t type, struct Expression *expr) pPatch->nType = type; fstk_DumpToStr(pPatch->tzFilename, sizeof(pPatch->tzFilename)); pPatch->nLine = nLineNo; - pPatch->nOffset = nPC; + pPatch->nOffset = pCurrentSection->nPC; while ((rpndata = rpn_PopByte(expr)) != 0xDEAD) { switch (rpndata) { @@ -480,52 +413,6 @@ void createpatch(uint32_t type, struct Expression *expr) 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 (!sect_HasData(pCurrentSection->nType)) { - 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); - uint32_t newSize = pCurrentSection->nPC + delta_size; - - if (newSize > 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, reached 0x%X).", - pCurrentSection->pzName, maxSize, newSize); - } -} - /* * Write an objectfile */ @@ -571,399 +458,3 @@ void out_SetFileName(char *s) if (CurrentOptions.verbose) printf("Output filename %s\n", s); } - -struct Section *out_FindSectionByName(const char *pzName) -{ - struct Section *pSect = pSectionList; - - while (pSect) { - if (strcmp(pzName, pSect->pzName) == 0) - return pSect; - - pSect = pSect->pNext; - } - - return 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 = out_FindSectionByName(pzName); - - if (pSect) { - 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"); - } - - pSect = malloc(sizeof(struct Section)); - if (pSect == NULL) - fatalerror("Not enough memory for section"); - - pSect->pzName = strdup(pzName); - if (pSect->pzName == NULL) - fatalerror("Not enough memory for sectionname"); - - // Force the bank to be 0 if that's the only possibility - switch (secttype) { - case SECTTYPE_ROM0: - case SECTTYPE_WRAM0: - case SECTTYPE_OAM: - case SECTTYPE_HRAM: - bank = 0; - } - - pSect->nType = secttype; - pSect->nPC = 0; - pSect->nOrg = org; - pSect->nBank = bank; - pSect->nAlign = alignment; - pSect->pNext = pSectionList; - pSect->pPatches = NULL; - - /* It is only needed to allocate memory for ROM sections. */ - if (sect_HasData(secttype)) { - 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; - } - - /* - * Add the new section to the list - * at the beginning because order doesn't matter - */ - pSectionList = pSect; - - 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 != NULL) ? pSect->nPC : 0; - - pPCSymbol->nValue = nPC; - pPCSymbol->pSection = pCurrentSection; - pPCSymbol->isConstant = pSect && pSect->nOrg != -1; -} - -/* - * 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; - pCurrentSection->tData[nPC] = b; - pCurrentSection->nPC++; - nPC++; - pPCSymbol->nValue++; -} - -/* - * 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 (!sect_HasData(pCurrentSection->nType)) { - 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_isKnown(expr)) { - pCurrentSection->tData[nPC] = 0; - createpatch(PATCHTYPE_BYTE, expr); - pCurrentSection->nPC++; - nPC++; - pPCSymbol->nValue++; - } else { - out_AbsByte(expr->nVal); - } - rpn_Free(expr); -} - -/* - * Output an absolute word - */ -void out_AbsWord(int32_t b) -{ - checkcodesection(); - checksectionoverflow(2); - b &= 0xFFFF; - 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) -{ - checkcodesection(); - checksectionoverflow(2); - if (!rpn_isKnown(expr)) { - pCurrentSection->tData[nPC] = 0; - pCurrentSection->tData[nPC + 1] = 0; - createpatch(PATCHTYPE_WORD, expr); - pCurrentSection->nPC += 2; - nPC += 2; - pPCSymbol->nValue += 2; - } else { - out_AbsWord(expr->nVal); - } - rpn_Free(expr); -} - -/* - * Output an absolute longword - */ -void out_AbsLong(int32_t b) -{ - checkcodesection(); - checksectionoverflow(sizeof(int32_t)); - 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) -{ - checkcodesection(); - checksectionoverflow(4); - if (!rpn_isKnown(expr)) { - pCurrentSection->tData[nPC] = 0; - pCurrentSection->tData[nPC + 1] = 0; - pCurrentSection->tData[nPC + 2] = 0; - pCurrentSection->tData[nPC + 3] = 0; - createpatch(PATCHTYPE_LONG, expr); - pCurrentSection->nPC += 4; - nPC += 4; - pPCSymbol->nValue += 4; - } else { - out_AbsLong(expr->nVal); - } - rpn_Free(expr); -} - -/* - * Output a PC-relative relocatable byte. Checking will be done to see if it - * is an absolute value in disguise. - */ -void out_PCRelByte(struct Expression *expr) -{ - checkcodesection(); - checksectionoverflow(1); - if (!rpn_isKnown(expr) || pCurrentSection->nOrg == -1) { - pCurrentSection->tData[nPC] = 0; - createpatch(PATCHTYPE_JR, expr); - pCurrentSection->nPC++; - nPC++; - pPCSymbol->nValue++; - } else { - /* Target is relative to the byte *after* the operand */ - uint16_t address = pCurrentSection->nOrg + nPC + 1; - /* The offset wraps (jump from ROM to HRAM, for loopexample) */ - int16_t offset = expr->nVal - address; - - if (offset < -128 || offset > 127) { - yyerror("jr target out of reach (expected -129 < %d < 128)", offset); - out_AbsByte(0); - } else { - out_AbsByte(offset); - } - } - rpn_Free(expr); -} - -/* - * Output a binary file - */ -void out_BinaryFile(char *s) -{ - FILE *f; - - f = fstk_FindFile(s, NULL); - if (f == NULL) { - if (oGeneratedMissingIncludes) { - oFailedOnMissingInclude = true; - return; - } - 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); - - 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, NULL); - if (f == NULL) { - if (oGeneratedMissingIncludes) { - oFailedOnMissingInclude = true; - return; - } - 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); - - 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); -} diff --git a/src/asm/rgbasm.5 b/src/asm/rgbasm.5 index 71fae3db..fc2925de 100644 --- a/src/asm/rgbasm.5 +++ b/src/asm/rgbasm.5 @@ -296,6 +296,66 @@ will push the current section context on the section stack. can then later be used to restore it. Useful for defining sections in included files when you don't want to destroy the section context for the program that included your file. The number of entries in the stack is limited only by the amount of memory in your machine. +.Ss RAM Code +Sometimes you want to have some code in RAM. +But then you can't simply put it in a RAM section, you have to store it in ROM and copy it to RAM at some time. +.Pp +This means the code (or data) will not be stored in the place it gets executed. +Luckily, +.Ic LOAD +blocks are the perfect solution to that. +Here's an example of how to use them: +.Bd -literal -offset indent + SECTION "LOAD example", ROMX + CopyCode: + ld de, RAMCode + ld hl, RAMLocation + ld c, RAMLocation.end - RAMLocation + .loop + ld a, [de] + inc de + ld [hli], a + dec c + jr nz, .loop + ret + + RAMCode: + LOAD "RAM code", WRAM0 + RAMLocation: + ld hl, .string + ld de, $9864 + .copy + ld a, [hli] + ld [de], a + inc de + and a + jr nz, .copy + ret + + .string + db "Hello World!", 0 + .end + ENDL +.Ed +.Pp +A +.Ic LOAD +block feels similar to a +.Ic SECTION +declaration because it creates a new one. +All data and code generated within such a block is placed in the current section like usual, but all labels are created as if the it was placed in this newly-created section. +.Pp +In the example above, all of the code and data will end up in the "LOAD example" section. +You will notice the +.Ic RAMCode +and +.Ic RAMLocation +labels. +The former is situated in ROM, where the code is stored, the latter in RAM, where the code will be loaded. +.Pp +You cannot nest +.Ic LOAD +blocks, nor can you change the current section within them. .Sh SYMBOLS .Pp .Ss Symbols diff --git a/src/asm/rpn.c b/src/asm/rpn.c index aceed207..6b634467 100644 --- a/src/asm/rpn.c +++ b/src/asm/rpn.c @@ -19,8 +19,8 @@ #include "asm/asm.h" #include "asm/main.h" #include "asm/rpn.h" +#include "asm/section.h" #include "asm/symbol.h" -#include "asm/output.h" #include "asm/warning.h" /* Makes an expression "not known", also setting its error message */ diff --git a/src/asm/section.c b/src/asm/section.c new file mode 100644 index 00000000..8cb67ba7 --- /dev/null +++ b/src/asm/section.c @@ -0,0 +1,553 @@ + +#include +#include +#include +#include + +#include "asm/fstack.h" +#include "asm/main.h" +#include "asm/output.h" +#include "asm/rpn.h" +#include "asm/section.h" +#include "asm/warning.h" + +#include "extern/err.h" + +struct SectionStackEntry { + struct Section *pSection; + struct sSymbol *pScope; /* Section's symbol scope */ + struct SectionStackEntry *pNext; +}; + +struct SectionStackEntry *pSectionStack; +static struct Section *currentLoadSection = NULL; + +/* + * 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 (!sect_HasData(pCurrentSection->nType)) + 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 = maxsize[pCurrentSection->nType]; + uint32_t newSize = pCurrentSection->nPC + delta_size; + + if (newSize > 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, reached 0x%X).", + pCurrentSection->pzName, maxSize, newSize); + } +} + +struct Section *out_FindSectionByName(const char *pzName) +{ + struct Section *pSect = pSectionList; + + while (pSect) { + if (strcmp(pzName, pSect->pzName) == 0) + return pSect; + + pSect = pSect->pNext; + } + + return NULL; +} + +/* + * Find a section by name and type. If it doesn't exist, create it + */ +static struct Section *getSection(char const *pzName, enum SectionType type, + int32_t org, int32_t bank, int32_t alignment) +{ + if (bank != -1) { + if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM + && type != SECTTYPE_SRAM && type != SECTTYPE_WRAMX) + yyerror("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections"); + else if (bank < bankranges[type][0] + || bank > bankranges[type][1]) + yyerror("%s bank value $%x out of range ($%x to $%x)", + typeNames[type], bank, + bankranges[type][0], bankranges[type][1]); + } + + if (alignment != 1) { + /* It doesn't make sense to have both set */ + uint32_t mask = alignment - 1; + + if (org != -1) { + if (org & mask) + yyerror("Section \"%s\"'s fixed address doesn't match its alignment", + pzName); + else + alignment = 1; /* Ignore it if it's satisfied */ + } + } + + if (org != -1) { + if (org < startaddr[type] || org > endaddr(type)) + yyerror("Section \"%s\"'s fixed address %#x is outside of range [%#x; %#x]", + pzName, org, startaddr[type], endaddr(type)); + } + + struct Section *pSect = out_FindSectionByName(pzName); + + if (pSect) { + if (type == 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"); + } + + pSect = malloc(sizeof(*pSect)); + if (pSect == NULL) + fatalerror("Not enough memory for section"); + + pSect->pzName = strdup(pzName); + if (pSect->pzName == NULL) + fatalerror("Not enough memory for sectionname"); + + if (nbbanks(type) == 1) + bank = bankranges[type][0]; + + pSect->nType = type; + pSect->nPC = 0; + pSect->nOrg = org; + pSect->nBank = bank; + pSect->nAlign = alignment; + pSect->pNext = pSectionList; + pSect->pPatches = NULL; + + /* It is only needed to allocate memory for ROM sections. */ + if (sect_HasData(type)) { + uint32_t sectsize; + + sectsize = maxsize[type]; + pSect->tData = malloc(sectsize); + if (pSect->tData == NULL) + fatalerror("Not enough memory for section"); + } else { + pSect->tData = NULL; + } + + /* + * Add the new section to the list + * at the beginning because order doesn't matter + */ + pSectionList = pSect; + + return pSect; +} + +/* + * Set the current section + */ +static void setSection(struct Section *pSect) +{ + if (nUnionDepth > 0) + fatalerror("Cannot change the section within a UNION"); + + nPC = (pSect != NULL) ? pSect->nPC : 0; + + pPCSymbol->pSection = pSect; + pPCSymbol->isConstant = pSect && pSect->nOrg != -1; +} + +/* + * Set the current section by name and type + */ +void out_NewSection(char const *pzName, uint32_t type, int32_t org, + struct SectionSpec const *attributes) +{ + if (currentLoadSection) + fatalerror("Cannot change the section within a `LOAD` block"); + + struct Section *pSect = getSection(pzName, type, org, attributes->bank, + 1 << attributes->alignment); + + nPC = pSect->nPC; + setSection(pSect); + pCurrentSection = pSect; +} + +/* + * Set the current section by name and type + */ +void out_SetLoadSection(char const *name, uint32_t type, int32_t org, + struct SectionSpec const *attributes) +{ + if (currentLoadSection) + fatalerror("`LOAD` blocks cannot be nested"); + + struct Section *pSect = getSection(name, type, org, attributes->bank, + 1 << attributes->alignment); + + nPC = pSect->nPC; + setSection(pSect); + currentLoadSection = pSect; +} + +void out_EndLoadSection(void) +{ + if (!currentLoadSection) + yyerror("Found `ENDL` outside of a `LOAD` block"); + currentLoadSection = NULL; + sym_SetCurrentSymbolScope(NULL); + + nPC = pCurrentSection->nPC; + setSection(pCurrentSection); +} + +struct Section *sect_GetSymbolSection(void) +{ + return currentLoadSection ? currentLoadSection : pCurrentSection; +} + +/* + * Output an absolute byte (bypassing ROM/union checks) + */ +static void absByteBypassCheck(uint8_t b) +{ + pCurrentSection->tData[pCurrentSection->nPC++] = b; + if (currentLoadSection) + currentLoadSection->nPC++; + nPC++; +} + +/* + * Output an absolute byte + */ +void out_AbsByte(uint8_t b) +{ + checkcodesection(); + checksectionoverflow(1); + absByteBypassCheck(b); +} + +void out_AbsByteGroup(uint8_t const *s, int32_t length) +{ + checkcodesection(); + checksectionoverflow(length); + while (length--) + absByteBypassCheck(*s++); +} + +/* + * Skip this many bytes + */ +void out_Skip(int32_t skip) +{ + checksection(); + checksectionoverflow(skip); + if (!sect_HasData(pCurrentSection->nType)) { + pCurrentSection->nPC += skip; + if (currentLoadSection) + currentLoadSection->nPC += skip; + nPC += skip; + } else if (nUnionDepth > 0) { + while (skip--) + absByteBypassCheck(CurrentOptions.fillchar); + } else { + checkcodesection(); + while (skip--) + absByteBypassCheck(CurrentOptions.fillchar); + } +} + +/* + * Output a NULL terminated string (excluding the NULL-character) + */ +void out_String(char const *s) +{ + checkcodesection(); + checksectionoverflow(strlen(s)); + while (*s) + absByteBypassCheck(*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) +{ + if (!rpn_isKnown(expr)) { + out_CreatePatch(PATCHTYPE_BYTE, expr); + out_AbsByte(0); + } else { + out_AbsByte(expr->nVal); + } + rpn_Free(expr); +} + +/* + * Output an absolute word + */ +static void absWord(uint16_t b) +{ + checkcodesection(); + checksectionoverflow(2); + pCurrentSection->tData[pCurrentSection->nPC++] = b & 0xFF; + pCurrentSection->tData[pCurrentSection->nPC++] = b >> 8; + if (currentLoadSection) + currentLoadSection->nPC += 2; + nPC += 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) +{ + if (!rpn_isKnown(expr)) { + out_CreatePatch(PATCHTYPE_WORD, expr); + absWord(0); + } else { + absWord(expr->nVal); + } + rpn_Free(expr); +} + +/* + * Output an absolute longword + */ +static void absLong(uint32_t b) +{ + checkcodesection(); + checksectionoverflow(4); + pCurrentSection->tData[pCurrentSection->nPC++] = b & 0xFF; + pCurrentSection->tData[pCurrentSection->nPC++] = b >> 8; + pCurrentSection->tData[pCurrentSection->nPC++] = b >> 16; + pCurrentSection->tData[pCurrentSection->nPC++] = b >> 24; + if (currentLoadSection) + currentLoadSection->nPC += 4; + nPC += 4; +} + +/* + * Output a relocatable longword. Checking will be done to see if + * is an absolute value in disguise. + */ +void out_RelLong(struct Expression *expr) +{ + if (!rpn_isKnown(expr)) { + out_CreatePatch(PATCHTYPE_LONG, expr); + absLong(0); + } else { + absLong(expr->nVal); + } + rpn_Free(expr); +} + +/* + * Output a PC-relative relocatable byte. Checking will be done to see if it + * is an absolute value in disguise. + */ +void out_PCRelByte(struct Expression *expr) +{ + checkcodesection(); + checksectionoverflow(1); + if (!rpn_isKnown(expr) || pCurrentSection->nOrg == -1) { + out_CreatePatch(PATCHTYPE_JR, expr); + pCurrentSection->tData[pCurrentSection->nPC++] = 0; + if (currentLoadSection) + currentLoadSection->nPC++; + nPC++; + } else { + /* Target is relative to the byte *after* the operand */ + uint16_t address = pCurrentSection->nOrg + nPC + 1; + /* The offset wraps (jump from ROM to HRAM, for loopexample) */ + int16_t offset = expr->nVal - address; + + if (offset < -128 || offset > 127) { + yyerror("jr target out of reach (expected -129 < %d < 128)", offset); + out_AbsByte(0); + } else { + out_AbsByte(offset); + } + } + rpn_Free(expr); +} + +/* + * Output a binary file + */ +void out_BinaryFile(char const *s) +{ + FILE *f = fstk_FindFile(s, NULL); + + if (!f) { + if (oGeneratedMissingIncludes) { + oFailedOnMissingInclude = true; + return; + } + fatalerror("Error opening INCBIN file '%s': %s", s, + strerror(errno)); + } + + int32_t fsize = -1; + int byte; + + checkcodesection(); + if (fseek(f, 0, SEEK_END) != -1) { + fsize = ftell(f); + rewind(f); + + checksectionoverflow(fsize); + } else if (errno != ESPIPE) { + yyerror("Error determining size of INCBIN file '%s': %s", s, + strerror(errno)); + } + + while ((byte = fgetc(f)) != EOF) { + if (fsize == -1) + checksectionoverflow(1); + pCurrentSection->tData[pCurrentSection->nPC++] = byte; + if (currentLoadSection) + currentLoadSection->nPC++; + nPC++; + } + + if (ferror(f)) + yyerror("Error reading INCBIN file '%s': %s", s, + strerror(errno)); + + fclose(f); +} + +void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length) +{ + if (start_pos < 0) { + yyerror("Start position cannot be negative (%d)", start_pos); + start_pos = 0; + } + + if (length < 0) { + yyerror("Number of bytes to read cannot be negative (%d)", + length); + length = 0; + } + if (length == 0) /* Don't even bother with 0-byte slices */ + return; + + FILE *f = fstk_FindFile(s, NULL); + + if (!f) { + if (oGeneratedMissingIncludes) { + oFailedOnMissingInclude = true; + return; + } + fatalerror("Error opening INCBIN file '%s': %s", s, + strerror(errno)); + } + + checkcodesection(); + checksectionoverflow(length); + + int32_t fsize; + + if (fseek(f, 0, SEEK_END) != -1) { + fsize = ftell(f); + + if (start_pos >= fsize) { + yyerror("Specified start position is greater than length of file"); + return; + } + + if ((start_pos + length) > fsize) + fatalerror("Specified range in INCBIN is out of bounds"); + + fseek(f, start_pos, SEEK_SET); + } else { + if (errno != ESPIPE) + yyerror("Error determining size of INCBIN file '%s': %s", + s, strerror(errno)); + /* The file isn't seekable, so we'll just skip bytes */ + while (start_pos--) + (void)fgetc(f); + } + + int32_t todo = length; + + while (todo--) { + int byte = fgetc(f); + + if (byte != EOF) { + pCurrentSection->tData[pCurrentSection->nPC++] = byte; + if (currentLoadSection) + currentLoadSection->nPC++; + nPC++; + } else if (ferror(f)) { + yyerror("Error reading INCBIN file '%s': %s", s, + strerror(errno)); + } else { + yyerror("Premature end of file (%d bytes left to read)", + todo + 1); + } + } + + fclose(f); +} + +/* + * 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; + setSection(pSect->pSection); + pCurrentSection = pSect->pSection; + sym_SetCurrentSymbolScope(pSect->pScope); + pSectionStack = pSect->pNext; + free(pSect); +} diff --git a/src/asm/symbol.c b/src/asm/symbol.c index 83452eb7..c0646b5f 100644 --- a/src/asm/symbol.c +++ b/src/asm/symbol.c @@ -21,7 +21,7 @@ #include "asm/symbol.h" #include "asm/main.h" #include "asm/mymath.h" -#include "asm/output.h" +#include "asm/section.h" #include "asm/util.h" #include "asm/warning.h" @@ -64,6 +64,11 @@ static int32_t Callback__LINE__(unused_ struct sSymbol const *sym) return nLineNo; } +static int32_t CallbackPC(struct sSymbol const *self) +{ + return self->pSection ? self->pSection->nOrg + self->pSection->nPC : 0; +} + /* * Get the nValue field of a symbol */ @@ -573,7 +578,7 @@ void sym_AddReloc(char const *tzSym) nsym->isExported = true; nsym->pScope = scope; - nsym->pSection = pCurrentSection; + nsym->pSection = sect_GetSymbolSection(); /* Labels need to be assigned a section, except PC */ if (!pCurrentSection && strcmp(tzSym, "@")) yyerror("Label \"%s\" created outside of a SECTION", @@ -722,6 +727,7 @@ void sym_Init(void) sym_AddReloc("@"); pPCSymbol = findsymbol("@", NULL); + pPCSymbol->Callback = CallbackPC; sym_AddEqu("_NARG", 0); p_NARGSymbol = findsymbol("_NARG", NULL); p_NARGSymbol->Callback = Callback_NARG; diff --git a/src/link/main.c b/src/link/main.c index d08aee9e..3095e799 100644 --- a/src/link/main.c +++ b/src/link/main.c @@ -177,10 +177,10 @@ int main(int argc, char *argv[]) } /* Patch the size array depending on command-line options */ - if (is32kMode) - maxsize[SECTTYPE_ROM0] = 0x8000; - if (isWRA0Mode) - maxsize[SECTTYPE_WRAM0] = 0x2000; + if (!is32kMode) + maxsize[SECTTYPE_ROM0] = 0x4000; + if (!isWRA0Mode) + maxsize[SECTTYPE_WRAM0] = 0x1000; /* Patch the bank ranges array depending on command-line options */ if (isDmgMode) diff --git a/src/linkdefs.c b/src/linkdefs.c index 4a3d9b30..c60221fa 100644 --- a/src/linkdefs.c +++ b/src/linkdefs.c @@ -13,11 +13,11 @@ uint16_t startaddr[] = { }; uint16_t maxsize[] = { - [SECTTYPE_ROM0] = 0x4000, + [SECTTYPE_ROM0] = 0x8000, [SECTTYPE_ROMX] = 0x4000, [SECTTYPE_VRAM] = 0x2000, [SECTTYPE_SRAM] = 0x2000, - [SECTTYPE_WRAM0] = 0x1000, + [SECTTYPE_WRAM0] = 0x2000, [SECTTYPE_WRAMX] = 0x1000, [SECTTYPE_OAM] = 0x00A0, [SECTTYPE_HRAM] = 0x007F diff --git a/test/link/fixed-oob.asm b/test/asm/fixed-oob.asm similarity index 100% rename from test/link/fixed-oob.asm rename to test/asm/fixed-oob.asm diff --git a/test/asm/fixed-oob.err b/test/asm/fixed-oob.err new file mode 100644 index 00000000..eac02cf2 --- /dev/null +++ b/test/asm/fixed-oob.err @@ -0,0 +1,15 @@ +ERROR: fixed-oob.asm(1): + Section "ROM0"'s fixed address 0xbabe is outside of range [0; 0x7fff] +ERROR: fixed-oob.asm(3): + Section "ROMX"'s fixed address 0xbeef is outside of range [0x4000; 0x7fff] +ERROR: fixed-oob.asm(5): + Section "VRAM"'s fixed address 0xc0de is outside of range [0x8000; 0x9fff] +ERROR: fixed-oob.asm(7): + Section "SRAM"'s fixed address 0xcafe is outside of range [0xa000; 0xbfff] +ERROR: fixed-oob.asm(11): + Section "WRAMX"'s fixed address 0xdad is outside of range [0xd000; 0xdfff] +ERROR: fixed-oob.asm(13): + Section "OAM"'s fixed address 0xcab is outside of range [0xfe00; 0xfe9f] +ERROR: fixed-oob.asm(15): + Section "HRAM"'s fixed address 0xbad is outside of range [0xff80; 0xfffe] +error: Assembly aborted (7 errors)! diff --git a/test/asm/fixed-oob.out b/test/asm/fixed-oob.out new file mode 100644 index 00000000..e69de29b diff --git a/test/asm/ram-code.asm b/test/asm/ram-code.asm new file mode 100644 index 00000000..e832e78d --- /dev/null +++ b/test/asm/ram-code.asm @@ -0,0 +1,20 @@ +SECTION "test", ROM0[1] + call Target + LOAD "new", WRAM0[$C001] +Target: dl $DEADBEEF +.end + ENDL +After: + jp Target + ld hl, Word + dw Byte, Target.end, After + +SECTION "ram test", WRAM0 ; Should end up at $C005 +Word: + dw + +SECTION "small ram test", WRAM0 ; Should end up at $C000 +Byte: + db + + PRINTT "{Target}\n{Target.end}\n{After}\n" diff --git a/test/asm/ram-code.err b/test/asm/ram-code.err new file mode 100644 index 00000000..e69de29b diff --git a/test/asm/ram-code.out b/test/asm/ram-code.out new file mode 100644 index 00000000..ad509cfc --- /dev/null +++ b/test/asm/ram-code.out @@ -0,0 +1,3 @@ +$C001 +$C005 +$8 diff --git a/test/asm/ram-code.out.bin b/test/asm/ram-code.out.bin new file mode 100644 index 00000000..0c2d78c7 Binary files /dev/null and b/test/asm/ram-code.out.bin differ diff --git a/test/link/fixed-oob.out b/test/link/fixed-oob.out deleted file mode 100644 index 3270d159..00000000 --- a/test/link/fixed-oob.out +++ /dev/null @@ -1,14 +0,0 @@ -warning: Section "VRAM"'s fixed address 0xc0de is outside of range [0x8000; 0x9fff] -warning: Section "VRAM"'s end address 0xc0de is greater than last address 0xa000 -warning: Section "OAM"'s fixed address 0xcab is outside of range [0xfe00; 0xfe9f] -warning: Section "WRAM0"'s fixed address 0xdead is outside of range [0xc000; 0xcfff] -warning: Section "WRAM0"'s end address 0xdead is greater than last address 0xd000 -warning: Section "HRAM"'s fixed address 0xbad is outside of range [0xff80; 0xfffe] -warning: Section "SRAM"'s fixed address 0xcafe is outside of range [0xa000; 0xbfff] -warning: Section "SRAM"'s end address 0xcafe is greater than last address 0xc000 -warning: Section "WRAMX"'s fixed address 0xdad is outside of range [0xd000; 0xdfff] -warning: Section "ROMX"'s fixed address 0xbeef is outside of range [0x4000; 0x7fff] -warning: Section "ROMX"'s end address 0xbeef is greater than last address 0x8000 -warning: Section "ROM0"'s fixed address 0xbabe is outside of range [0; 0x3fff] -warning: Section "ROM0"'s end address 0xbabe is greater than last address 0x4000 -error: Sanity checks failed