mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Implement ds align[alignment, offset] (#1181)
This commit is contained in:
@@ -84,6 +84,11 @@ int yylex(void);
|
|||||||
bool lexer_CaptureRept(struct CaptureBody *capture);
|
bool lexer_CaptureRept(struct CaptureBody *capture);
|
||||||
bool lexer_CaptureMacroBody(struct CaptureBody *capture);
|
bool lexer_CaptureMacroBody(struct CaptureBody *capture);
|
||||||
|
|
||||||
|
struct DsAlignment {
|
||||||
|
uint8_t alignment;
|
||||||
|
uint16_t alignOfs;
|
||||||
|
};
|
||||||
|
|
||||||
#define INITIAL_DS_ARG_SIZE 2
|
#define INITIAL_DS_ARG_SIZE 2
|
||||||
struct DsArgList {
|
struct DsArgList {
|
||||||
size_t nbArgs;
|
size_t nbArgs;
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ void sect_EndLoadSection(void);
|
|||||||
struct Section *sect_GetSymbolSection(void);
|
struct Section *sect_GetSymbolSection(void);
|
||||||
uint32_t sect_GetSymbolOffset(void);
|
uint32_t sect_GetSymbolOffset(void);
|
||||||
uint32_t sect_GetOutputOffset(void);
|
uint32_t sect_GetOutputOffset(void);
|
||||||
|
uint32_t sect_GetAlignBytes(uint8_t alignment, uint16_t offset);
|
||||||
void sect_AlignPC(uint8_t alignment, uint16_t offset);
|
void sect_AlignPC(uint8_t alignment, uint16_t offset);
|
||||||
|
|
||||||
void sect_StartUnion(void);
|
void sect_StartUnion(void);
|
||||||
|
|||||||
42
man/rgbasm.5
42
man/rgbasm.5
@@ -681,14 +681,14 @@ Place the section at an address whose
|
|||||||
.Ar align
|
.Ar align
|
||||||
least-significant bits are equal to
|
least-significant bits are equal to
|
||||||
.Ar offset .
|
.Ar offset .
|
||||||
(Note that
|
Note that
|
||||||
.Ic ALIGN Ns Bq Ar align
|
.Ic ALIGN Ns Bq Ar align
|
||||||
is a shorthand for
|
is a shorthand for
|
||||||
.Ic ALIGN Ns Bq Ar align , No 0 ) .
|
.Ic ALIGN Ns Bq Ar align , No 0 .
|
||||||
This option can be used with
|
This option can be used with
|
||||||
.Bq Ar addr ,
|
.Bq Ar addr ,
|
||||||
as long as they don't contradict eachother.
|
as long as they don't contradict each other.
|
||||||
It's also possible to request alignment in the middle of a section, see
|
It's also possible to request alignment in the middle of a section; see
|
||||||
.Sx Requesting alignment
|
.Sx Requesting alignment
|
||||||
below.
|
below.
|
||||||
.El
|
.El
|
||||||
@@ -869,7 +869,7 @@ or
|
|||||||
.Ic ROMX .
|
.Ic ROMX .
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
Different declarations of the same unionized section are not appended, but instead overlaid on top of eachother, just like
|
Different declarations of the same unionized section are not appended, but instead overlaid on top of each other, just like
|
||||||
.Sx Unions .
|
.Sx Unions .
|
||||||
Similarly, the size of an unionized section is the largest of all its declarations.
|
Similarly, the size of an unionized section is the largest of all its declarations.
|
||||||
.Ss Section fragments
|
.Ss Section fragments
|
||||||
@@ -1433,6 +1433,27 @@ In ROM sections, it will be filled with the value passed to the
|
|||||||
.Fl p
|
.Fl p
|
||||||
command-line option, except when using overlays with
|
command-line option, except when using overlays with
|
||||||
.Fl O .
|
.Fl O .
|
||||||
|
.Pp
|
||||||
|
Instead of an exact number of bytes, you can specify
|
||||||
|
.Ic ALIGN Ns Bq Ar align , offset
|
||||||
|
to allocate however many bytes are required to align the subsequent data.
|
||||||
|
Thus,
|
||||||
|
.Sq Ic DS ALIGN Ns Bo Ar align , offset Bc , No ...
|
||||||
|
is equivalent to
|
||||||
|
.Sq Ic DS Ar n , No ...
|
||||||
|
followed by
|
||||||
|
.Sq Ic ALIGN Ns Bq Ar align , offset ,
|
||||||
|
where
|
||||||
|
.Ar n
|
||||||
|
is the minimum value needed to satisfy the
|
||||||
|
.Ic ALIGN
|
||||||
|
constraint (see
|
||||||
|
.Sx Requesting alignment
|
||||||
|
below).
|
||||||
|
Note that
|
||||||
|
.Ic ALIGN Ns Bq Ar align
|
||||||
|
is a shorthand for
|
||||||
|
.Ic ALIGN Ns Bq Ar align , No 0 .
|
||||||
.Ss Defining constant data in ROM
|
.Ss Defining constant data in ROM
|
||||||
.Ic DB
|
.Ic DB
|
||||||
defines a list of bytes that will be stored in the final image.
|
defines a list of bytes that will be stored in the final image.
|
||||||
@@ -2137,6 +2158,17 @@ Note that
|
|||||||
.Ic ALIGN Ar align
|
.Ic ALIGN Ar align
|
||||||
is a shorthand for
|
is a shorthand for
|
||||||
.Ic ALIGN Ar align , No 0 .
|
.Ic ALIGN Ar align , No 0 .
|
||||||
|
.Pp
|
||||||
|
There may be times when you don't just want to specify an alignment constraint at the current location, but also skip ahead until the constraint can be satisfied.
|
||||||
|
In that case, you can use
|
||||||
|
.Ic DS ALIGN Ns Bq Ar align , offset
|
||||||
|
to allocate however many bytes are required to align the subsequent data.
|
||||||
|
.Pp
|
||||||
|
If the constraint cannot be met by skipping any amount of space, an error is produced.
|
||||||
|
Note that
|
||||||
|
.Ic ALIGN Ns Bq Ar align
|
||||||
|
is a shorthand for
|
||||||
|
.Ic ALIGN Ns Bq Ar align , No 0 .
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
.Xr rgbasm 1 ,
|
.Xr rgbasm 1 ,
|
||||||
.Xr rgblink 1 ,
|
.Xr rgblink 1 ,
|
||||||
|
|||||||
@@ -503,6 +503,7 @@ enum {
|
|||||||
struct SectionSpec sectSpec;
|
struct SectionSpec sectSpec;
|
||||||
struct MacroArgs *macroArg;
|
struct MacroArgs *macroArg;
|
||||||
enum AssertionType assertType;
|
enum AssertionType assertType;
|
||||||
|
struct DsAlignment dsAlign;
|
||||||
struct DsArgList dsArgs;
|
struct DsArgList dsArgs;
|
||||||
struct PurgeArgList purgeArgs;
|
struct PurgeArgList purgeArgs;
|
||||||
struct {
|
struct {
|
||||||
@@ -660,6 +661,7 @@ enum {
|
|||||||
%type <sectMod> sectmod
|
%type <sectMod> sectmod
|
||||||
%type <macroArg> macroargs
|
%type <macroArg> macroargs
|
||||||
|
|
||||||
|
%type <dsAlign> ds_align
|
||||||
%type <dsArgs> ds_args
|
%type <dsArgs> ds_args
|
||||||
|
|
||||||
%type <purgeArgs> purge_args
|
%type <purgeArgs> purge_args
|
||||||
@@ -1194,6 +1196,40 @@ ds : T_POP_DS uconst { sect_Skip($2, true); }
|
|||||||
sect_RelBytes($2, $4.args, $4.nbArgs);
|
sect_RelBytes($2, $4.args, $4.nbArgs);
|
||||||
freeDsArgList(&$4);
|
freeDsArgList(&$4);
|
||||||
}
|
}
|
||||||
|
| T_POP_DS ds_align trailing_comma {
|
||||||
|
uint32_t n = sect_GetAlignBytes($2.alignment, $2.alignOfs);
|
||||||
|
|
||||||
|
sect_Skip(n, true);
|
||||||
|
sect_AlignPC($2.alignment, $2.alignOfs);
|
||||||
|
}
|
||||||
|
| T_POP_DS ds_align T_COMMA ds_args trailing_comma {
|
||||||
|
uint32_t n = sect_GetAlignBytes($2.alignment, $2.alignOfs);
|
||||||
|
|
||||||
|
sect_RelBytes(n, $4.args, $4.nbArgs);
|
||||||
|
sect_AlignPC($2.alignment, $2.alignOfs);
|
||||||
|
freeDsArgList(&$4);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
ds_align : T_OP_ALIGN T_LBRACK uconst T_RBRACK {
|
||||||
|
if ($3 > 16) {
|
||||||
|
error("Alignment must be between 0 and 16, not %u\n", $3);
|
||||||
|
} else {
|
||||||
|
$$.alignment = $3;
|
||||||
|
$$.alignOfs = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
| T_OP_ALIGN T_LBRACK uconst T_COMMA uconst T_RBRACK {
|
||||||
|
if ($3 > 16) {
|
||||||
|
error("Alignment must be between 0 and 16, not %u\n", $3);
|
||||||
|
} else if ($5 >= 1 << $3) {
|
||||||
|
error("Offset must be between 0 and %u, not %u\n",
|
||||||
|
(1 << $3) - 1, $5);
|
||||||
|
} else {
|
||||||
|
$$.alignment = $3;
|
||||||
|
$$.alignOfs = $5;
|
||||||
|
}
|
||||||
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
ds_args : reloc_8bit {
|
ds_args : reloc_8bit {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
@@ -471,6 +472,27 @@ uint32_t sect_GetOutputOffset(void)
|
|||||||
return curOffset + loadOffset;
|
return curOffset + loadOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns how many bytes need outputting for the specified alignment and offset to succeed
|
||||||
|
uint32_t sect_GetAlignBytes(uint8_t alignment, uint16_t offset)
|
||||||
|
{
|
||||||
|
struct Section *sect = sect_GetSymbolSection();
|
||||||
|
if (!sect)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
bool isFixed = sect->org != (uint32_t)-1;
|
||||||
|
|
||||||
|
// If the section is not aligned, no bytes are needed
|
||||||
|
// (fixed sections count as being maximally aligned for this purpose)
|
||||||
|
uint8_t curAlignment = isFixed ? 16 : sect->align;
|
||||||
|
if (curAlignment == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// We need `(pcValue + curOffset + return value) % (1 << alignment) == offset`
|
||||||
|
uint16_t pcValue = isFixed ? sect->org : sect->alignOfs;
|
||||||
|
return static_cast<uint16_t>(offset - curOffset - pcValue)
|
||||||
|
% (1u << std::min(alignment, curAlignment));
|
||||||
|
}
|
||||||
|
|
||||||
void sect_AlignPC(uint8_t alignment, uint16_t offset)
|
void sect_AlignPC(uint8_t alignment, uint16_t offset)
|
||||||
{
|
{
|
||||||
if (!checksection())
|
if (!checksection())
|
||||||
@@ -480,20 +502,16 @@ void sect_AlignPC(uint8_t alignment, uint16_t offset)
|
|||||||
uint16_t alignSize = 1 << alignment; // Size of an aligned "block"
|
uint16_t alignSize = 1 << alignment; // Size of an aligned "block"
|
||||||
|
|
||||||
if (sect->org != (uint32_t)-1) {
|
if (sect->org != (uint32_t)-1) {
|
||||||
if ((sym_GetPCValue() - offset) % alignSize)
|
if ((sect->org + curOffset - offset) % alignSize)
|
||||||
error("Section's fixed address fails required alignment (PC = $%04" PRIx32
|
error("Section's fixed address fails required alignment (PC = $%04" PRIx32
|
||||||
")\n", sym_GetPCValue());
|
")\n", sect->org + curOffset);
|
||||||
} else if (sect->align != 0) {
|
} else if (sect->align != 0 && (((sect->alignOfs + curOffset) % (1u << sect->align))
|
||||||
if ((((sect->alignOfs + curOffset) % (1 << sect->align)) - offset) % alignSize) {
|
- offset) % alignSize) {
|
||||||
error("Section's alignment fails required alignment (offset from section start = $%04"
|
error("Section's alignment fails required alignment (offset from section start = $%04"
|
||||||
PRIx32 ")\n", curOffset);
|
PRIx32 ")\n", curOffset);
|
||||||
} else if (alignment > sect->align) {
|
} else if (alignment > sect->align) {
|
||||||
sect->align = alignment;
|
sect->align = alignment;
|
||||||
sect->alignOfs = (offset - curOffset) % alignSize;
|
// We need `(sect->alignOfs + curOffset) % alignSize == offset`
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sect->align = alignment;
|
|
||||||
// We need `(sect->alignOfs + curOffset) % alignSize == offset
|
|
||||||
sect->alignOfs = (offset - curOffset) % alignSize;
|
sect->alignOfs = (offset - curOffset) % alignSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
test/asm/ds-align-min.asm
Normal file
7
test/asm/ds-align-min.asm
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
SECTION "test", ROM0
|
||||||
|
align 3 ; 3 < 4, so this is used in `ds align` below
|
||||||
|
db 1, 2, 5 ; three
|
||||||
|
ds align[4] ; (1 << 3) - three = 5 bytes
|
||||||
|
db 10, 20 ; two
|
||||||
|
align 4, 2
|
||||||
|
assert @ - STARTOF("test") == 3 + 5 + 2
|
||||||
0
test/asm/ds-align-min.err
Normal file
0
test/asm/ds-align-min.err
Normal file
0
test/asm/ds-align-min.out
Normal file
0
test/asm/ds-align-min.out
Normal file
BIN
test/asm/ds-align-min.out.bin
Normal file
BIN
test/asm/ds-align-min.out.bin
Normal file
Binary file not shown.
7
test/asm/ds-align-offset.asm
Normal file
7
test/asm/ds-align-offset.asm
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
SECTION "test", ROM0
|
||||||
|
align 3, 3 ; 3 < 4, so this is used in `ds align` below
|
||||||
|
db 2, 22, 222 ; three
|
||||||
|
ds align[4, 4] ; (1 << 3) - 3 - three + 4 = 6 bytes
|
||||||
|
db 42 ; one
|
||||||
|
align 4, 5
|
||||||
|
assert @ - STARTOF("test") == 3 + 6 + 1
|
||||||
0
test/asm/ds-align-offset.err
Normal file
0
test/asm/ds-align-offset.err
Normal file
0
test/asm/ds-align-offset.out
Normal file
0
test/asm/ds-align-offset.out
Normal file
BIN
test/asm/ds-align-offset.out.bin
Normal file
BIN
test/asm/ds-align-offset.out.bin
Normal file
Binary file not shown.
25
test/asm/ds-align.asm
Normal file
25
test/asm/ds-align.asm
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
SECTION "aligned", ROM0, ALIGN[8]
|
||||||
|
db 1, 2, 3, 4, 5 ; five
|
||||||
|
ds align[8, $ff], 6 ; (1 << 8) - five - one = 250 bytes
|
||||||
|
db 7 ; one
|
||||||
|
align 8
|
||||||
|
assert @ - STARTOF("aligned") == 5 + 250 + 1
|
||||||
|
|
||||||
|
SECTION "fixed", ROM0[$100]
|
||||||
|
ds align[2] ; already aligned, 0 bytes
|
||||||
|
db 8, 9, 10 ; three
|
||||||
|
ds align[4, $e], 11 ; (1 << 4) - three - two = 11 bytes
|
||||||
|
db 12, 13 ; two
|
||||||
|
align 4
|
||||||
|
assert @ - STARTOF("fixed") == 3 + 11 + 2
|
||||||
|
|
||||||
|
SECTION "floating", ROM0
|
||||||
|
db 14, 15 ; two
|
||||||
|
ds align[4] ; not aligned, 0 bytes
|
||||||
|
align 4 ; redundant
|
||||||
|
db 16, 17, 18 ; three
|
||||||
|
align 4, 3
|
||||||
|
ds align[5], 19, 20 ; (1 << 4) - three = 13 bytes
|
||||||
|
db 21 ; one
|
||||||
|
align 5, 1
|
||||||
|
assert @ - STARTOF("floating") == 2 + 3 + 13 + 1
|
||||||
0
test/asm/ds-align.err
Normal file
0
test/asm/ds-align.err
Normal file
0
test/asm/ds-align.out
Normal file
0
test/asm/ds-align.out
Normal file
BIN
test/asm/ds-align.out.bin
Normal file
BIN
test/asm/ds-align.out.bin
Normal file
Binary file not shown.
Reference in New Issue
Block a user