Implement ds align[alignment, offset] (#1181)

This commit is contained in:
Rangi
2023-11-21 17:57:47 -05:00
committed by GitHub
parent 6f0ffcf3e1
commit 46e29de66f
17 changed files with 148 additions and 17 deletions

View File

@@ -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;

View File

@@ -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);

View File

@@ -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 ,

View File

@@ -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 {

View File

@@ -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;
} }
} }

View 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

View File

View File

Binary file not shown.

View 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

View File

View File

Binary file not shown.

25
test/asm/ds-align.asm Normal file
View 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
View File

0
test/asm/ds-align.out Normal file
View File

BIN
test/asm/ds-align.out.bin Normal file

Binary file not shown.