diff --git a/include/asm/asm.h b/include/asm/asm.h index 1bf97e79..231433aa 100644 --- a/include/asm/asm.h +++ b/include/asm/asm.h @@ -18,19 +18,23 @@ #include "asm/localasm.h" +#define MAXUNIONS 128 +#define MAXMACROARGS 256 +#define MAXINCPATHS 128 + extern SLONG nLineNo; extern ULONG nTotalLines; extern ULONG nPC; extern ULONG nPass; extern ULONG nIFDepth; extern bool skipElif; +extern ULONG nUnionDepth; +extern ULONG unionStart[MAXUNIONS]; +extern ULONG unionSize[MAXUNIONS]; extern char tzCurrentFileName[_MAX_PATH + 1]; extern struct Section *pCurrentSection; extern struct sSymbol *tHashedSymbols[HASHSIZE]; extern struct sSymbol *pPCSymbol; extern bool oDontExpandStrings; -#define MAXMACROARGS 256 -#define MAXINCPATHS 128 - #endif /* // ASM_H */ diff --git a/src/asm/asmy.y b/src/asm/asmy.y index aff6737c..a4612e52 100644 --- a/src/asm/asmy.y +++ b/src/asm/asmy.y @@ -431,6 +431,34 @@ void if_skip_to_endc() nLineNo--; } +void startUnion() { + if (!pCurrentSection) { + fatalerror("UNIONs must be inside a SECTION"); + } + + ULONG unionIndex = nUnionDepth; + nUnionDepth++; + if (nUnionDepth > MAXUNIONS) { + fatalerror("Too many nested UNIONs"); + } + + unionStart[unionIndex] = nPC; + unionSize[unionIndex] = 0; +} + +void updateUnion() { + ULONG unionIndex = nUnionDepth - 1; + ULONG size = nPC - unionStart[unionIndex]; + + if (size > unionSize[unionIndex]) { + unionSize[unionIndex] = size; + } + + nPC = unionStart[unionIndex]; + pCurrentSection->nPC = unionStart[unionIndex]; + pPCSymbol->nValue = unionStart[unionIndex]; +} + %} %union @@ -506,6 +534,7 @@ void if_skip_to_endc() %token T_POP_MACRO %token T_POP_ENDM %token T_POP_RSRESET T_POP_RSSET +%token T_POP_UNION T_POP_NEXTU T_POP_ENDU %token T_POP_INCBIN T_POP_REPT %token T_POP_CHARMAP %token T_POP_SHIFT @@ -644,6 +673,9 @@ simple_pseudoop : include | section | rsreset | rsset + | union + | nextu + | endu | incbin | charmap | rept @@ -745,6 +777,31 @@ rb : T_LABEL T_POP_RB uconst } ; +union : T_POP_UNION { + startUnion(); + }; + +nextu : T_POP_NEXTU { + if (nUnionDepth <= 0) { + fatalerror("Found NEXTU outside of a UNION construct"); + } + + updateUnion(); + }; + +endu : T_POP_ENDU { + if (nUnionDepth <= 0) { + fatalerror("Found ENDU outside of a UNION construct"); + } + + updateUnion(); + + nUnionDepth--; + nPC = unionStart[nUnionDepth] + unionSize[nUnionDepth]; + pCurrentSection->nPC = nPC; + pPCSymbol->nValue = nPC; + }; + ds : T_POP_DS uconst { out_Skip( $2 ); } ; diff --git a/src/asm/globlex.c b/src/asm/globlex.c index 6cc74ebd..60703a32 100644 --- a/src/asm/globlex.c +++ b/src/asm/globlex.c @@ -331,6 +331,10 @@ struct sLexInitString staticstrings[] = { {"else", T_POP_ELSE}, {"elif", T_POP_ELIF}, {"endc", T_POP_ENDC}, + + {"union", T_POP_UNION}, + {"nextu", T_POP_NEXTU}, + {"endu", T_POP_ENDU}, {"wram0", T_SECT_WRAM0}, {"vram", T_SECT_VRAM}, diff --git a/src/asm/main.c b/src/asm/main.c index e54b25fc..c35bf528 100644 --- a/src/asm/main.c +++ b/src/asm/main.c @@ -22,8 +22,9 @@ char **cldefines; clock_t nStartClock, nEndClock; SLONG nLineNo; -ULONG nTotalLines, nPass, nPC, nIFDepth, nErrors; +ULONG nTotalLines, nPass, nPC, nIFDepth, nUnionDepth, nErrors; bool skipElif; +ULONG unionStart[128], unionSize[128]; extern int yydebug; @@ -416,6 +417,7 @@ main(int argc, char *argv[]) nTotalLines = 0; nIFDepth = 0; skipElif = true; + nUnionDepth = 0; nPC = 0; nPass = 1; nErrors = 0; @@ -438,11 +440,16 @@ main(int argc, char *argv[]) if (nIFDepth != 0) { errx(1, "Unterminated IF construct (%ld levels)!", nIFDepth); } + + if (nUnionDepth != 0) { + errx(1, "Unterminated UNION construct (%ld levels)!", nUnionDepth); + } nTotalLines = 0; nLineNo = 1; nIFDepth = 0; skipElif = true; + nUnionDepth = 0; nPC = 0; nPass = 2; nErrors = 0; diff --git a/src/asm/output.c b/src/asm/output.c index fce458bf..34501f78 100644 --- a/src/asm/output.c +++ b/src/asm/output.c @@ -460,6 +460,8 @@ checkcodesection(void) 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"); } } @@ -613,6 +615,10 @@ out_FindSection(char *pzName, ULONG secttype, SLONG org, SLONG bank, SLONG align void out_SetCurrentSection(struct Section * pSect) { + if (nUnionDepth > 0) { + fatalerror("Cannot change the section within a UNION"); + } + pCurrentSection = pSect; nPC = pSect->nPC; @@ -651,12 +657,11 @@ out_NewAlignedSection(char *pzName, ULONG secttype, SLONG alignment, SLONG bank) } /* - * Output an absolute byte + * Output an absolute byte (bypassing ROM/union checks) */ void -out_AbsByte(int b) +out_AbsByteBypassCheck(int b) { - checkcodesection(); checksectionoverflow(1); b &= 0xFF; if (nPass == 2) @@ -667,6 +672,16 @@ out_AbsByte(int b) pPCSymbol->nValue += 1; } +/* + * Output an absolute byte + */ +void +out_AbsByte(int b) +{ + checkcodesection(); + out_AbsByteBypassCheck(b); +} + void out_AbsByteGroup(char *s, int length) { @@ -689,6 +704,9 @@ out_Skip(int skip) pCurrentSection->nPC += skip; nPC += skip; pPCSymbol->nValue += skip; + } else if (nUnionDepth > 0) { + while (skip--) + out_AbsByteBypassCheck(CurrentOptions.fillchar); } else { checkcodesection(); while (skip--) diff --git a/src/asm/rgbasm.5 b/src/asm/rgbasm.5 index d45cbafd..34b5a501 100644 --- a/src/asm/rgbasm.5 +++ b/src/asm/rgbasm.5 @@ -612,6 +612,41 @@ You can also include only part of a file with The example below includes 256 bytes from data.bin starting from byte 78. .Pp .Dl INCBIN \[dq]data.bin\[dq],78,256 +.Ss Unions +Unions allow multiple memory allocations to share the same space in memory, +like unions in C. +This allows you to easily reuse memory for different purposes, depending on +the game's state. +.Pp +You create unions using the +.Ic UNION , +.Ic NEXTU +and +.Ic ENDU +keywords. +.Ic NEXTU +lets you create a new block of allocations, and you may use it as many times +within a union as necessary. +.Pp +.Bd -literal -offset indent +UNION +Name: ds 8 +Nickname: ds 8 +NEXTU +Health: dw +Something: ds 3 +Lives: db +NEXTU +Temporary: ds 19 +ENDU +.Ed +.Pp +This union will use up 19 bytes, as this is the size of the largest block +(the last one, containing 'Temporary'). +Of course, as 'Name', 'Health', and 'Temporary' all point to the same memory +locations, writes to any one of these will affect values read from the others. +.Pp +Unions may be used in any section, but code and data may not be included. .Sh THE MACRO LANGUAGE .Pp .Ss Printing things during assembly