From f8bbe9be48a5b485c79c16bc260ddb86c04b711a Mon Sep 17 00:00:00 2001 From: Ben10do Date: Thu, 20 Jul 2017 19:17:03 +0100 Subject: [PATCH] Add support for unions Unions allow multiple memory allocations (using ds, etc.) to share the same space in memory. This allows games to use the same memory for different purposes, depending on their state. This also adds documentation on how to use the new UNION, NEXTU, and ENDU keywords. --- include/asm/asm.h | 10 ++++++--- src/asm/asmy.y | 57 +++++++++++++++++++++++++++++++++++++++++++++++ src/asm/globlex.c | 4 ++++ src/asm/main.c | 9 +++++++- src/asm/output.c | 24 +++++++++++++++++--- src/asm/rgbasm.5 | 35 +++++++++++++++++++++++++++++ 6 files changed, 132 insertions(+), 7 deletions(-) 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