ref: f8bbe9be48a5b485c79c16bc260ddb86c04b711a
parent: 4d01b2d5ac3ed6f2b2f0a66794ade2cda272df20
author: Ben10do <[email protected]>
date: Thu Jul 20 15:17:03 EDT 2017
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.
--- a/include/asm/asm.h
+++ b/include/asm/asm.h
@@ -18,6 +18,10 @@
#include "asm/localasm.h"
+#define MAXUNIONS 128
+#define MAXMACROARGS 256
+#define MAXINCPATHS 128
+
extern SLONG nLineNo;
extern ULONG nTotalLines;
extern ULONG nPC;
@@ -24,13 +28,13 @@
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 */
--- a/src/asm/asmy.y
+++ b/src/asm/asmy.y
@@ -431,6 +431,34 @@
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 @@
%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 @@
| section
| rsreset
| rsset
+ | union
+ | nextu
+ | endu
| incbin
| charmap
| rept
@@ -744,6 +776,31 @@
sym_AddSet( "_RS", sym_GetConstantValue("_RS")+$3 );
}
;
+
+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 ); }
--- a/src/asm/globlex.c
+++ b/src/asm/globlex.c
@@ -331,6 +331,10 @@
{"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},
--- a/src/asm/main.c
+++ b/src/asm/main.c
@@ -22,8 +22,9 @@
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 @@
nTotalLines = 0;
nIFDepth = 0;
skipElif = true;
+ nUnionDepth = 0;
nPC = 0;
nPass = 1;
nErrors = 0;
@@ -438,11 +440,16 @@
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;
--- a/src/asm/output.c
+++ b/src/asm/output.c
@@ -460,6 +460,8 @@
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 @@
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 @@
}
/*
- * 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,7 +672,17 @@
pPCSymbol->nValue += 1;
}
+/*
+ * Output an absolute byte
+ */
void
+out_AbsByte(int b)
+{
+ checkcodesection();
+ out_AbsByteBypassCheck(b);
+}
+
+void
out_AbsByteGroup(char *s, int length)
{
checkcodesection();
@@ -689,6 +704,9 @@
pCurrentSection->nPC += skip;
nPC += skip;
pPCSymbol->nValue += skip;
+ } else if (nUnionDepth > 0) {
+ while (skip--)
+ out_AbsByteBypassCheck(CurrentOptions.fillchar);
} else {
checkcodesection();
while (skip--)
--- a/src/asm/rgbasm.5
+++ b/src/asm/rgbasm.5
@@ -612,6 +612,41 @@
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