mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
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.
This commit is contained in:
committed by
Antonio Niño Díaz
parent
4d01b2d5ac
commit
f8bbe9be48
@@ -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 */
|
||||
|
||||
@@ -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 ); }
|
||||
;
|
||||
|
||||
@@ -332,6 +332,10 @@ struct sLexInitString staticstrings[] = {
|
||||
{"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},
|
||||
{"romx", T_SECT_ROMX},
|
||||
|
||||
@@ -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;
|
||||
@@ -439,10 +441,15 @@ main(int argc, char *argv[])
|
||||
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;
|
||||
|
||||
@@ -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--)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user