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:
Ben10do
2017-07-20 19:17:03 +01:00
committed by Antonio Niño Díaz
parent 4d01b2d5ac
commit f8bbe9be48
6 changed files with 132 additions and 7 deletions

View File

@@ -18,19 +18,23 @@
#include "asm/localasm.h" #include "asm/localasm.h"
#define MAXUNIONS 128
#define MAXMACROARGS 256
#define MAXINCPATHS 128
extern SLONG nLineNo; extern SLONG nLineNo;
extern ULONG nTotalLines; extern ULONG nTotalLines;
extern ULONG nPC; extern ULONG nPC;
extern ULONG nPass; extern ULONG nPass;
extern ULONG nIFDepth; extern ULONG nIFDepth;
extern bool skipElif; extern bool skipElif;
extern ULONG nUnionDepth;
extern ULONG unionStart[MAXUNIONS];
extern ULONG unionSize[MAXUNIONS];
extern char tzCurrentFileName[_MAX_PATH + 1]; extern char tzCurrentFileName[_MAX_PATH + 1];
extern struct Section *pCurrentSection; extern struct Section *pCurrentSection;
extern struct sSymbol *tHashedSymbols[HASHSIZE]; extern struct sSymbol *tHashedSymbols[HASHSIZE];
extern struct sSymbol *pPCSymbol; extern struct sSymbol *pPCSymbol;
extern bool oDontExpandStrings; extern bool oDontExpandStrings;
#define MAXMACROARGS 256
#define MAXINCPATHS 128
#endif /* // ASM_H */ #endif /* // ASM_H */

View File

@@ -431,6 +431,34 @@ void if_skip_to_endc()
nLineNo--; 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 %union
@@ -506,6 +534,7 @@ void if_skip_to_endc()
%token T_POP_MACRO %token T_POP_MACRO
%token T_POP_ENDM %token T_POP_ENDM
%token T_POP_RSRESET T_POP_RSSET %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_INCBIN T_POP_REPT
%token T_POP_CHARMAP %token T_POP_CHARMAP
%token T_POP_SHIFT %token T_POP_SHIFT
@@ -644,6 +673,9 @@ simple_pseudoop : include
| section | section
| rsreset | rsreset
| rsset | rsset
| union
| nextu
| endu
| incbin | incbin
| charmap | charmap
| rept | 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 ds : T_POP_DS uconst
{ out_Skip( $2 ); } { out_Skip( $2 ); }
; ;

View File

@@ -332,6 +332,10 @@ struct sLexInitString staticstrings[] = {
{"elif", T_POP_ELIF}, {"elif", T_POP_ELIF},
{"endc", T_POP_ENDC}, {"endc", T_POP_ENDC},
{"union", T_POP_UNION},
{"nextu", T_POP_NEXTU},
{"endu", T_POP_ENDU},
{"wram0", T_SECT_WRAM0}, {"wram0", T_SECT_WRAM0},
{"vram", T_SECT_VRAM}, {"vram", T_SECT_VRAM},
{"romx", T_SECT_ROMX}, {"romx", T_SECT_ROMX},

View File

@@ -22,8 +22,9 @@ char **cldefines;
clock_t nStartClock, nEndClock; clock_t nStartClock, nEndClock;
SLONG nLineNo; SLONG nLineNo;
ULONG nTotalLines, nPass, nPC, nIFDepth, nErrors; ULONG nTotalLines, nPass, nPC, nIFDepth, nUnionDepth, nErrors;
bool skipElif; bool skipElif;
ULONG unionStart[128], unionSize[128];
extern int yydebug; extern int yydebug;
@@ -416,6 +417,7 @@ main(int argc, char *argv[])
nTotalLines = 0; nTotalLines = 0;
nIFDepth = 0; nIFDepth = 0;
skipElif = true; skipElif = true;
nUnionDepth = 0;
nPC = 0; nPC = 0;
nPass = 1; nPass = 1;
nErrors = 0; nErrors = 0;
@@ -439,10 +441,15 @@ main(int argc, char *argv[])
errx(1, "Unterminated IF construct (%ld levels)!", nIFDepth); errx(1, "Unterminated IF construct (%ld levels)!", nIFDepth);
} }
if (nUnionDepth != 0) {
errx(1, "Unterminated UNION construct (%ld levels)!", nUnionDepth);
}
nTotalLines = 0; nTotalLines = 0;
nLineNo = 1; nLineNo = 1;
nIFDepth = 0; nIFDepth = 0;
skipElif = true; skipElif = true;
nUnionDepth = 0;
nPC = 0; nPC = 0;
nPass = 2; nPass = 2;
nErrors = 0; nErrors = 0;

View File

@@ -460,6 +460,8 @@ checkcodesection(void)
pCurrentSection->nType != SECT_ROMX) { pCurrentSection->nType != SECT_ROMX) {
fatalerror("Section '%s' cannot contain code or data (not ROM0 or ROMX)", fatalerror("Section '%s' cannot contain code or data (not ROM0 or ROMX)",
pCurrentSection->pzName); 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 void
out_SetCurrentSection(struct Section * pSect) out_SetCurrentSection(struct Section * pSect)
{ {
if (nUnionDepth > 0) {
fatalerror("Cannot change the section within a UNION");
}
pCurrentSection = pSect; pCurrentSection = pSect;
nPC = pSect->nPC; 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 void
out_AbsByte(int b) out_AbsByteBypassCheck(int b)
{ {
checkcodesection();
checksectionoverflow(1); checksectionoverflow(1);
b &= 0xFF; b &= 0xFF;
if (nPass == 2) if (nPass == 2)
@@ -667,6 +672,16 @@ out_AbsByte(int b)
pPCSymbol->nValue += 1; pPCSymbol->nValue += 1;
} }
/*
* Output an absolute byte
*/
void
out_AbsByte(int b)
{
checkcodesection();
out_AbsByteBypassCheck(b);
}
void void
out_AbsByteGroup(char *s, int length) out_AbsByteGroup(char *s, int length)
{ {
@@ -689,6 +704,9 @@ out_Skip(int skip)
pCurrentSection->nPC += skip; pCurrentSection->nPC += skip;
nPC += skip; nPC += skip;
pPCSymbol->nValue += skip; pPCSymbol->nValue += skip;
} else if (nUnionDepth > 0) {
while (skip--)
out_AbsByteBypassCheck(CurrentOptions.fillchar);
} else { } else {
checkcodesection(); checkcodesection();
while (skip--) while (skip--)

View File

@@ -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. The example below includes 256 bytes from data.bin starting from byte 78.
.Pp .Pp
.Dl INCBIN \[dq]data.bin\[dq],78,256 .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 .Sh THE MACRO LANGUAGE
.Pp .Pp
.Ss Printing things during assembly .Ss Printing things during assembly