diff --git a/include/linkdefs.h b/include/linkdefs.h index e8fd56cb..590bfa76 100644 --- a/include/linkdefs.h +++ b/include/linkdefs.h @@ -14,7 +14,7 @@ #define RGBDS_OBJECT_VERSION_STRING "RGB%1u" #define RGBDS_OBJECT_VERSION_NUMBER 9U -#define RGBDS_OBJECT_REV 8U +#define RGBDS_OBJECT_REV 9U enum AssertionType { ASSERT_WARN, @@ -49,6 +49,7 @@ enum RPNCommand { RPN_SHL = 0x40, RPN_SHR = 0x41, + RPN_USHR = 0x42, RPN_BANK_SYM = 0x50, RPN_BANK_SECT = 0x51, diff --git a/include/opmath.h b/include/opmath.h index 0c83b83c..b1804f0b 100644 --- a/include/opmath.h +++ b/include/opmath.h @@ -16,5 +16,6 @@ int32_t op_modulo(int32_t dividend, int32_t divisor); int32_t op_exponent(int32_t base, uint32_t power); int32_t op_shift_left(int32_t value, int32_t amount); int32_t op_shift_right(int32_t value, int32_t amount); +int32_t op_shift_right_unsigned(int32_t value, int32_t amount); #endif /* RGBDS_OP_MATH_H */ diff --git a/src/asm/lexer.c b/src/asm/lexer.c index 5741d0ff..a06dd4c0 100644 --- a/src/asm/lexer.c +++ b/src/asm/lexer.c @@ -1890,18 +1890,23 @@ static int yylex_NORMAL(void) return T_OP_LOGICLT; } - case '>': /* Either >>=, GT, GTE, or right shift */ + case '>': /* Either >>=, GT, GTE, or either kind of right shift */ switch (peek()) { case '=': shiftChar(); return T_OP_LOGICGE; case '>': shiftChar(); - if (peek() == '=') { + switch (peek()) { + case '=': shiftChar(); return T_POP_SHREQ; + case '>': + shiftChar(); + return T_OP_USHR; + default: + return T_OP_SHR; } - return T_OP_SHR; default: return T_OP_LOGICGT; } diff --git a/src/asm/parser.y b/src/asm/parser.y index 87ea3c22..2acddcc1 100644 --- a/src/asm/parser.y +++ b/src/asm/parser.y @@ -534,7 +534,7 @@ enum { %token T_OP_LOGICNE "!=" T_OP_LOGICEQU "==" %token T_OP_ADD "+" T_OP_SUB "-" %token T_OP_OR "|" T_OP_XOR "^" T_OP_AND "&" -%token T_OP_SHL "<<" T_OP_SHR ">>" +%token T_OP_SHL "<<" T_OP_SHR ">>" T_OP_USHR ">>>" %token T_OP_MUL "*" T_OP_DIV "/" T_OP_MOD "%" %token T_OP_NOT "~" %left T_OP_LOGICOR @@ -542,7 +542,7 @@ enum { %left T_OP_LOGICGT T_OP_LOGICLT T_OP_LOGICGE T_OP_LOGICLE T_OP_LOGICNE T_OP_LOGICEQU %left T_OP_ADD T_OP_SUB %left T_OP_OR T_OP_XOR T_OP_AND -%left T_OP_SHL T_OP_SHR +%left T_OP_SHL T_OP_SHR T_OP_USHR %left T_OP_MUL T_OP_DIV T_OP_MOD %precedence NEG /* negation -- unary minus */ @@ -1477,6 +1477,9 @@ relocexpr_no_str : scoped_anon_id { rpn_Symbol(&$$, $1); } | relocexpr T_OP_SHR relocexpr { rpn_BinaryOp(RPN_SHR, &$$, &$1, &$3); } + | relocexpr T_OP_USHR relocexpr { + rpn_BinaryOp(RPN_USHR, &$$, &$1, &$3); + } | relocexpr T_OP_MUL relocexpr { rpn_BinaryOp(RPN_MUL, &$$, &$1, &$3); } diff --git a/src/asm/rgbasm.5 b/src/asm/rgbasm.5 index 23e39761..49f8f819 100644 --- a/src/asm/rgbasm.5 +++ b/src/asm/rgbasm.5 @@ -251,7 +251,9 @@ A great number of operators you can use in expressions are available (listed fro .It Li ** Ta Exponent .It Li ~ + - Ta Unary complement/plus/minus .It Li * / % Ta Multiply/divide/modulo -.It Li << >> Ta Shift left/right +.It Li << Ta Shift left +.It Li >> Ta Signed shift right (sign-extension) +.It Li >>> Ta Unsigned shift right (zero-extension) .It Li & \&| ^ Ta Binary and/or/xor .It Li + - Ta Add/subtract .It Li != == <= >= < > Ta Comparison diff --git a/src/asm/rpn.c b/src/asm/rpn.c index 2734627a..0db22073 100644 --- a/src/asm/rpn.c +++ b/src/asm/rpn.c @@ -421,6 +421,19 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr, expr->val = op_shift_right(src1->val, src2->val); break; + case RPN_USHR: + if (src2->val < 0) + warning(WARNING_SHIFT_AMOUNT, + "Shifting right by negative amount %" PRId32 "\n", + src2->val); + + if (src2->val >= 32) + warning(WARNING_SHIFT_AMOUNT, + "Shifting right by large amount %" PRId32 "\n", + src2->val); + + expr->val = op_shift_right_unsigned(src1->val, src2->val); + break; case RPN_MUL: expr->val = uleft * uright; break; diff --git a/src/link/patch.c b/src/link/patch.c index b767ca08..c8391288 100644 --- a/src/link/patch.c +++ b/src/link/patch.c @@ -265,6 +265,10 @@ static int32_t computeRPNExpr(struct Patch const *patch, value = popRPN(); value = op_shift_right(popRPN(), value); break; + case RPN_USHR: + value = popRPN(); + value = op_shift_right_unsigned(popRPN(), value); + break; case RPN_BANK_SYM: value = 0; diff --git a/src/opmath.c b/src/opmath.c index d1f6e335..11d1ea56 100644 --- a/src/opmath.c +++ b/src/opmath.c @@ -86,3 +86,18 @@ int32_t op_shift_right(int32_t value, int32_t amount) // undefined, so use a left shift manually sign-extended return ((uint32_t)value >> amount) | amount_high_bits; } + +int32_t op_shift_right_unsigned(int32_t value, int32_t amount) +{ + // Repeat the easy cases here to avoid INT_MIN funny business + if (amount == 0) + return value; + if (value == 0 || amount <= -32) + return 0; + if (amount > 31) + return (value < 0) ? -1 : 0; + if (amount < 0) + return op_shift_left(value, -amount); + + return (uint32_t)value >> amount; +} diff --git a/src/rgbds.5 b/src/rgbds.5 index 7958e884..ee97920b 100644 --- a/src/rgbds.5 +++ b/src/rgbds.5 @@ -241,6 +241,7 @@ with some bytes being special prefixes for integers and symbols. .It Li $35 Ta Li <= comparison .It Li $40 Ta Li << operator .It Li $41 Ta Li >> operator +.It Li $42 Ta Li >>> operator .It Li $50 Ta Li BANK(symbol) , a .Ar LONG diff --git a/test/asm/shift.asm b/test/asm/shift.asm index 7f5d2078..c4e9b2b7 100644 --- a/test/asm/shift.asm +++ b/test/asm/shift.asm @@ -1,6 +1,6 @@ macro test ; Test the rpn system, as well as the linker... - DEF expr EQUS STRRPL(STRRPL("\1 + zero)", "<<", "<< ("), ">>", ">> (") + DEF expr EQUS STRRPL(STRRPL("\1 + zero)", "<< ", "<< ("), ">> ", ">> (") dl expr PURGE expr @@ -24,5 +24,8 @@ section "test", ROM0[0] test -4 >> 2 test -1 >> -9001 + test $DEADBEEF >> 1 + test $DEADBEEF >>> 1 + SECTION "Zero", ROM0[0] zero: diff --git a/test/asm/shift.err b/test/asm/shift.err index 60c0c5bc..db23d16e 100644 --- a/test/asm/shift.err +++ b/test/asm/shift.err @@ -24,3 +24,5 @@ warning: shift.asm(25) -> shift.asm::test(8): [-Wshift] Shifting right negative value -1 warning: shift.asm(25) -> shift.asm::test(8): [-Wshift-amount] Shifting right by negative amount -9001 +warning: shift.asm(27) -> shift.asm::test(8): [-Wshift] + Shifting right negative value -559038737 diff --git a/test/asm/shift.out b/test/asm/shift.out index e3cbe553..9a5b3a7d 100644 --- a/test/asm/shift.out +++ b/test/asm/shift.out @@ -10,3 +10,5 @@ -4 >> 1 = $FFFFFFFE -4 >> 2 = $FFFFFFFF -1 >> -9001 = $0 +$DEADBEEF >> 1 = $EF56DF77 +$DEADBEEF >>> 1 = $6F56DF77 diff --git a/test/asm/shift.out.bin b/test/asm/shift.out.bin index 5aa2aef0..03e55714 100644 Binary files a/test/asm/shift.out.bin and b/test/asm/shift.out.bin differ