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_CaptureMacroBody(struct CaptureBody *capture);
|
||||
|
||||
struct DsAlignment {
|
||||
uint8_t alignment;
|
||||
uint16_t alignOfs;
|
||||
};
|
||||
|
||||
#define INITIAL_DS_ARG_SIZE 2
|
||||
struct DsArgList {
|
||||
size_t nbArgs;
|
||||
|
||||
@@ -47,6 +47,7 @@ void sect_EndLoadSection(void);
|
||||
struct Section *sect_GetSymbolSection(void);
|
||||
uint32_t sect_GetSymbolOffset(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_StartUnion(void);
|
||||
|
||||
42
man/rgbasm.5
42
man/rgbasm.5
@@ -681,14 +681,14 @@ Place the section at an address whose
|
||||
.Ar align
|
||||
least-significant bits are equal to
|
||||
.Ar offset .
|
||||
(Note that
|
||||
Note that
|
||||
.Ic ALIGN Ns Bq Ar align
|
||||
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
|
||||
.Bq Ar addr ,
|
||||
as long as they don't contradict eachother.
|
||||
It's also possible to request alignment in the middle of a section, see
|
||||
as long as they don't contradict each other.
|
||||
It's also possible to request alignment in the middle of a section; see
|
||||
.Sx Requesting alignment
|
||||
below.
|
||||
.El
|
||||
@@ -869,7 +869,7 @@ or
|
||||
.Ic ROMX .
|
||||
.El
|
||||
.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 .
|
||||
Similarly, the size of an unionized section is the largest of all its declarations.
|
||||
.Ss Section fragments
|
||||
@@ -1433,6 +1433,27 @@ In ROM sections, it will be filled with the value passed to the
|
||||
.Fl p
|
||||
command-line option, except when using overlays with
|
||||
.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
|
||||
.Ic DB
|
||||
defines a list of bytes that will be stored in the final image.
|
||||
@@ -2137,6 +2158,17 @@ Note that
|
||||
.Ic ALIGN Ar align
|
||||
is a shorthand for
|
||||
.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
|
||||
.Xr rgbasm 1 ,
|
||||
.Xr rgblink 1 ,
|
||||
|
||||
@@ -503,6 +503,7 @@ enum {
|
||||
struct SectionSpec sectSpec;
|
||||
struct MacroArgs *macroArg;
|
||||
enum AssertionType assertType;
|
||||
struct DsAlignment dsAlign;
|
||||
struct DsArgList dsArgs;
|
||||
struct PurgeArgList purgeArgs;
|
||||
struct {
|
||||
@@ -660,6 +661,7 @@ enum {
|
||||
%type <sectMod> sectmod
|
||||
%type <macroArg> macroargs
|
||||
|
||||
%type <dsAlign> ds_align
|
||||
%type <dsArgs> ds_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);
|
||||
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 {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
@@ -471,6 +472,27 @@ uint32_t sect_GetOutputOffset(void)
|
||||
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)
|
||||
{
|
||||
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"
|
||||
|
||||
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
|
||||
")\n", sym_GetPCValue());
|
||||
} else if (sect->align != 0) {
|
||||
if ((((sect->alignOfs + curOffset) % (1 << sect->align)) - offset) % alignSize) {
|
||||
error("Section's alignment fails required alignment (offset from section start = $%04"
|
||||
PRIx32 ")\n", curOffset);
|
||||
} else if (alignment > sect->align) {
|
||||
sect->align = alignment;
|
||||
sect->alignOfs = (offset - curOffset) % alignSize;
|
||||
}
|
||||
} else {
|
||||
")\n", sect->org + curOffset);
|
||||
} else if (sect->align != 0 && (((sect->alignOfs + curOffset) % (1u << sect->align))
|
||||
- offset) % alignSize) {
|
||||
error("Section's alignment fails required alignment (offset from section start = $%04"
|
||||
PRIx32 ")\n", curOffset);
|
||||
} else if (alignment > sect->align) {
|
||||
sect->align = alignment;
|
||||
// We need `(sect->alignOfs + curOffset) % alignSize == offset
|
||||
// We need `(sect->alignOfs + curOffset) % alignSize == offset`
|
||||
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