mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
Allow the bit/res/set bit index to be determined at link time (#1654)
This increments the object file revision number from 11 to 12 since it adds a new `RPN_BIT_INDEX` command.
This commit is contained in:
@@ -51,6 +51,7 @@ struct Expression {
|
|||||||
|
|
||||||
bool makeCheckHRAM();
|
bool makeCheckHRAM();
|
||||||
void makeCheckRST();
|
void makeCheckRST();
|
||||||
|
void makeCheckBitIndex(uint8_t mask);
|
||||||
|
|
||||||
void checkNBit(uint8_t n) const;
|
void checkNBit(uint8_t n) const;
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#include "helpers.hpp" // assume
|
#include "helpers.hpp" // assume
|
||||||
|
|
||||||
#define RGBDS_OBJECT_VERSION_STRING "RGB9"
|
#define RGBDS_OBJECT_VERSION_STRING "RGB9"
|
||||||
#define RGBDS_OBJECT_REV 11U
|
#define RGBDS_OBJECT_REV 12U
|
||||||
|
|
||||||
enum AssertionType { ASSERT_WARN, ASSERT_ERROR, ASSERT_FATAL };
|
enum AssertionType { ASSERT_WARN, ASSERT_ERROR, ASSERT_FATAL };
|
||||||
|
|
||||||
@@ -52,6 +52,7 @@ enum RPNCommand {
|
|||||||
|
|
||||||
RPN_HRAM = 0x60,
|
RPN_HRAM = 0x60,
|
||||||
RPN_RST = 0x61,
|
RPN_RST = 0x61,
|
||||||
|
RPN_BIT_INDEX = 0x62,
|
||||||
|
|
||||||
RPN_HIGH = 0x70,
|
RPN_HIGH = 0x70,
|
||||||
RPN_LOW = 0x71,
|
RPN_LOW = 0x71,
|
||||||
|
|||||||
13
man/rgbds.5
13
man/rgbds.5
@@ -388,10 +388,19 @@ The value is then ANDed with $00FF
|
|||||||
check.
|
check.
|
||||||
Checks if the value is a valid
|
Checks if the value is a valid
|
||||||
.Ql rst
|
.Ql rst
|
||||||
.Pq see Do RST vec Dc in Xr gbz80 7
|
vector
|
||||||
vector, that is one of $00, $08, $10, $18, $20, $28, $30, or $38.
|
.Pq see Do RST vec Dc in Xr gbz80 7 ,
|
||||||
|
that is, one of $00, $08, $10, $18, $20, $28, $30, or $38.
|
||||||
The value is then ORed with $C7
|
The value is then ORed with $C7
|
||||||
.Pq Ql \&| $C7 .
|
.Pq Ql \&| $C7 .
|
||||||
|
.It Li $62 Ta Ql bit/res/set
|
||||||
|
check; followed by the instruction's
|
||||||
|
.Cm BYTE
|
||||||
|
mask.
|
||||||
|
Checks if the value is a valid bit index
|
||||||
|
.Pq see e.g. Do BIT u3, r8 Dc in Xr gbz80 7 ,
|
||||||
|
that is, from 0 to 7.
|
||||||
|
The value is then ORed with the instruction's mask.
|
||||||
.It Li $80 Ta Integer literal; followed by the
|
.It Li $80 Ta Integer literal; followed by the
|
||||||
.Cm LONG
|
.Cm LONG
|
||||||
integer.
|
integer.
|
||||||
|
|||||||
@@ -325,6 +325,7 @@
|
|||||||
// `relocexpr_no_str` exists because strings usually count as numeric expressions, but some
|
// `relocexpr_no_str` exists because strings usually count as numeric expressions, but some
|
||||||
// contexts treat numbers and strings differently, e.g. `db "string"` or `print "string"`.
|
// contexts treat numbers and strings differently, e.g. `db "string"` or `print "string"`.
|
||||||
%type <Expression> relocexpr_no_str
|
%type <Expression> relocexpr_no_str
|
||||||
|
%type <Expression> reloc_3bit
|
||||||
%type <Expression> reloc_8bit
|
%type <Expression> reloc_8bit
|
||||||
%type <Expression> reloc_8bit_offset
|
%type <Expression> reloc_8bit_offset
|
||||||
%type <Expression> reloc_16bit
|
%type <Expression> reloc_16bit
|
||||||
@@ -333,7 +334,6 @@
|
|||||||
%type <int32_t> iconst
|
%type <int32_t> iconst
|
||||||
%type <int32_t> uconst
|
%type <int32_t> uconst
|
||||||
// Constant numbers used only in specific contexts
|
// Constant numbers used only in specific contexts
|
||||||
%type <int32_t> bit_const
|
|
||||||
%type <int32_t> precision_arg
|
%type <int32_t> precision_arg
|
||||||
%type <int32_t> rs_uconst
|
%type <int32_t> rs_uconst
|
||||||
%type <int32_t> sect_org
|
%type <int32_t> sect_org
|
||||||
@@ -1214,13 +1214,10 @@ print_expr:
|
|||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
bit_const:
|
reloc_3bit:
|
||||||
iconst {
|
relocexpr {
|
||||||
$$ = $1;
|
$$ = std::move($1);
|
||||||
if ($$ < 0 || $$ > 7) {
|
$$.checkNBit(3);
|
||||||
::error("Bit number must be between 0 and 7, not %" PRId32 "\n", $$);
|
|
||||||
$$ = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -1789,9 +1786,15 @@ sm83_and:
|
|||||||
;
|
;
|
||||||
|
|
||||||
sm83_bit:
|
sm83_bit:
|
||||||
SM83_BIT bit_const COMMA reg_r {
|
SM83_BIT reloc_3bit COMMA reg_r {
|
||||||
|
uint8_t mask = static_cast<uint8_t>(0x40 | $4);
|
||||||
|
$2.makeCheckBitIndex(mask);
|
||||||
sect_ConstByte(0xCB);
|
sect_ConstByte(0xCB);
|
||||||
sect_ConstByte(0x40 | ($2 << 3) | $4);
|
if (!$2.isKnown()) {
|
||||||
|
sect_RelByte($2, 0);
|
||||||
|
} else {
|
||||||
|
sect_ConstByte(mask | ($2.value() << 3));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -2103,9 +2106,15 @@ sm83_push:
|
|||||||
;
|
;
|
||||||
|
|
||||||
sm83_res:
|
sm83_res:
|
||||||
SM83_RES bit_const COMMA reg_r {
|
SM83_RES reloc_3bit COMMA reg_r {
|
||||||
|
uint8_t mask = static_cast<uint8_t>(0x80 | $4);
|
||||||
|
$2.makeCheckBitIndex(mask);
|
||||||
sect_ConstByte(0xCB);
|
sect_ConstByte(0xCB);
|
||||||
sect_ConstByte(0x80 | ($2 << 3) | $4);
|
if (!$2.isKnown()) {
|
||||||
|
sect_RelByte($2, 0);
|
||||||
|
} else {
|
||||||
|
sect_ConstByte(mask | ($2.value() << 3));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -2204,9 +2213,15 @@ sm83_scf:
|
|||||||
;
|
;
|
||||||
|
|
||||||
sm83_set:
|
sm83_set:
|
||||||
SM83_SET bit_const COMMA reg_r {
|
SM83_SET reloc_3bit COMMA reg_r {
|
||||||
|
uint8_t mask = static_cast<uint8_t>(0xC0 | $4);
|
||||||
|
$2.makeCheckBitIndex(mask);
|
||||||
sect_ConstByte(0xCB);
|
sect_ConstByte(0xCB);
|
||||||
sect_ConstByte(0xC0 | ($2 << 3) | $4);
|
if (!$2.isKnown()) {
|
||||||
|
sect_RelByte($2, 0);
|
||||||
|
} else {
|
||||||
|
sect_ConstByte(mask | ($2.value() << 3));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|||||||
@@ -355,6 +355,7 @@ void Expression::makeUnaryOp(RPNCommand op, Expression &&src) {
|
|||||||
case RPN_STARTOF_SECTTYPE:
|
case RPN_STARTOF_SECTTYPE:
|
||||||
case RPN_HRAM:
|
case RPN_HRAM:
|
||||||
case RPN_RST:
|
case RPN_RST:
|
||||||
|
case RPN_BIT_INDEX:
|
||||||
case RPN_CONST:
|
case RPN_CONST:
|
||||||
case RPN_SYM:
|
case RPN_SYM:
|
||||||
fatalerror("%d is not an unary operator\n", op);
|
fatalerror("%d is not an unary operator\n", op);
|
||||||
@@ -514,6 +515,7 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
|
|||||||
case RPN_STARTOF_SECTTYPE:
|
case RPN_STARTOF_SECTTYPE:
|
||||||
case RPN_HRAM:
|
case RPN_HRAM:
|
||||||
case RPN_RST:
|
case RPN_RST:
|
||||||
|
case RPN_BIT_INDEX:
|
||||||
case RPN_HIGH:
|
case RPN_HIGH:
|
||||||
case RPN_LOW:
|
case RPN_LOW:
|
||||||
case RPN_BITWIDTH:
|
case RPN_BITWIDTH:
|
||||||
@@ -608,6 +610,20 @@ void Expression::makeCheckRST() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Expression::makeCheckBitIndex(uint8_t mask) {
|
||||||
|
assume((mask & 0xC0) != 0x00); // The high two bits must correspond to BIT, RES, or SET
|
||||||
|
|
||||||
|
if (!isKnown()) {
|
||||||
|
uint8_t *ptr = reserveSpace(2);
|
||||||
|
*ptr++ = RPN_BIT_INDEX;
|
||||||
|
*ptr = mask;
|
||||||
|
} else if (int32_t val = value(); val & ~0x07) {
|
||||||
|
// A valid bit index must be masked with 0x07
|
||||||
|
static char const *instructions[4] = {"instruction", "BIT", "RES", "SET"};
|
||||||
|
error("Invalid bit index %" PRId32 " for %s\n", val, instructions[mask >> 6]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Checks that an RPN expression's value fits within N bits (signed or unsigned)
|
// Checks that an RPN expression's value fits within N bits (signed or unsigned)
|
||||||
void Expression::checkNBit(uint8_t n) const {
|
void Expression::checkNBit(uint8_t n) const {
|
||||||
if (isKnown()) {
|
if (isKnown()) {
|
||||||
|
|||||||
@@ -363,7 +363,6 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
case RPN_RST:
|
case RPN_RST:
|
||||||
value = popRPN(patch);
|
value = popRPN(patch);
|
||||||
// Acceptable values are 0x00, 0x08, 0x10, ..., 0x38
|
// Acceptable values are 0x00, 0x08, 0x10, ..., 0x38
|
||||||
// They can be easily checked with a bitmask
|
|
||||||
if (value & ~0x38) {
|
if (value & ~0x38) {
|
||||||
if (!isError) {
|
if (!isError) {
|
||||||
error(patch.src, patch.lineNo, "Value $%" PRIx32 " is not a RST vector", value);
|
error(patch.src, patch.lineNo, "Value $%" PRIx32 " is not a RST vector", value);
|
||||||
@@ -374,6 +373,21 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
value |= 0xC7;
|
value |= 0xC7;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case RPN_BIT_INDEX: {
|
||||||
|
value = popRPN(patch);
|
||||||
|
int32_t mask = getRPNByte(expression, size, patch);
|
||||||
|
// Acceptable values are 0 to 7
|
||||||
|
if (value & ~0x07) {
|
||||||
|
if (!isError) {
|
||||||
|
error(patch.src, patch.lineNo, "Value $%" PRIx32 " is not a bit index", value);
|
||||||
|
isError = true;
|
||||||
|
}
|
||||||
|
value = 0;
|
||||||
|
}
|
||||||
|
value = mask | (value << 3);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case RPN_CONST:
|
case RPN_CONST:
|
||||||
value = 0;
|
value = 0;
|
||||||
for (uint8_t shift = 0; shift < 32; shift += 8) {
|
for (uint8_t shift = 0; shift < 32; shift += 8) {
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ error: invalid-instructions.asm(5):
|
|||||||
syntax error, unexpected bc, expecting hl
|
syntax error, unexpected bc, expecting hl
|
||||||
error: invalid-instructions.asm(6):
|
error: invalid-instructions.asm(6):
|
||||||
syntax error, unexpected number, expecting hl
|
syntax error, unexpected number, expecting hl
|
||||||
|
warning: invalid-instructions.asm(7): [-Wtruncation]
|
||||||
|
Expression must be 3-bit
|
||||||
error: invalid-instructions.asm(7):
|
error: invalid-instructions.asm(7):
|
||||||
Bit number must be between 0 and 7, not 8
|
Invalid bit index 8 for BIT
|
||||||
error: invalid-instructions.asm(8):
|
error: invalid-instructions.asm(8):
|
||||||
Invalid address $40 for RST
|
Invalid address $40 for RST
|
||||||
error: Assembly aborted (8 errors)!
|
error: Assembly aborted (8 errors)!
|
||||||
|
|||||||
5
test/link/bit-res-set-bad.asm
Normal file
5
test/link/bit-res-set-bad.asm
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
SECTION "test", ROM0
|
||||||
|
Label:
|
||||||
|
bit Label - 1, a
|
||||||
|
res Label + 8, [hl]
|
||||||
|
set Label | $ff00, b
|
||||||
4
test/link/bit-res-set-bad.out
Normal file
4
test/link/bit-res-set-bad.out
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
error: bit-res-set-bad.asm(5): Value $ff00 is not a bit index
|
||||||
|
error: bit-res-set-bad.asm(4): Value $8 is not a bit index
|
||||||
|
error: bit-res-set-bad.asm(3): Value $ffffffff is not a bit index
|
||||||
|
Linking failed with 3 errors
|
||||||
10
test/link/bit-res-set.asm
Normal file
10
test/link/bit-res-set.asm
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
SECTION "test", ROM0
|
||||||
|
Label:
|
||||||
|
bit Label + 0, b
|
||||||
|
bit Label + 1, c
|
||||||
|
res Label + 2, d
|
||||||
|
res Label + 3, e
|
||||||
|
set Label + 4, h
|
||||||
|
set Label + 5, l
|
||||||
|
bit Label + 6, a
|
||||||
|
set Label + 7, [hl]
|
||||||
0
test/link/bit-res-set.out
Normal file
0
test/link/bit-res-set.out
Normal file
1
test/link/bit-res-set.out.bin
Normal file
1
test/link/bit-res-set.out.bin
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ֻ@ֻIֻ’ֻ›ֻהֻםֻwֻ
|
||||||
Reference in New Issue
Block a user