diff --git a/src/asm/parser.y b/src/asm/parser.y index d051685e..275e4585 100644 --- a/src/asm/parser.y +++ b/src/asm/parser.y @@ -99,6 +99,10 @@ // REG_AF == REG_SP since LD/INC/ADD/DEC allow SP, while PUSH/POP allow AF enum { REG_BC, REG_DE, REG_HL, REG_SP, REG_AF = REG_SP }; + // Names are not needed for AF or SP + static char const *reg_tt_names[] = { "BC", "DE", "HL" }; + static char const *reg_tt_high_names[] = { "B", "D", "H" }; + static char const *reg_tt_low_names[] = { "C", "E", "L" }; // CC_NZ == CC_Z ^ 1, and CC_NC == CC_C ^ 1, so `!` can toggle them enum { CC_NZ, CC_Z, CC_NC, CC_C }; @@ -380,6 +384,8 @@ %type reg_ss %type reg_rr %type reg_tt +%type reg_tt_no_af +%type reg_bc_or_de %type ccode_expr %type ccode %type op_a_n @@ -2115,16 +2121,30 @@ sm83_ld_hl: sect_ConstByte(0xF8); sect_RelByte($5, 1); } + | SM83_LD MODE_HL COMMA MODE_SP { + ::error("LD HL, SP is not a valid instruction; use LD HL, SP + 0\n"); + } | SM83_LD MODE_HL COMMA reloc_16bit { sect_ConstByte(0x01 | (REG_HL << 4)); sect_RelWord($4, 1); } + | SM83_LD MODE_HL COMMA reg_tt_no_af { + ::error( + "LD HL, %s is not a valid instruction; use LD H, %s and LD L, %s\n", + reg_tt_names[$4], + reg_tt_high_names[$4], + reg_tt_low_names[$4] + ); + } ; sm83_ld_sp: SM83_LD MODE_SP COMMA MODE_HL { sect_ConstByte(0xF9); } + | SM83_LD MODE_SP COMMA reg_bc_or_de { + ::error("LD SP, %s is not a valid instruction\n", reg_tt_names[$4]); + } | SM83_LD MODE_SP COMMA reloc_16bit { sect_ConstByte(0x01 | (REG_SP << 4)); sect_RelWord($4, 1); @@ -2197,13 +2217,20 @@ sm83_ld_a: ; sm83_ld_ss: - SM83_LD MODE_BC COMMA reloc_16bit { - sect_ConstByte(0x01 | (REG_BC << 4)); + SM83_LD reg_bc_or_de COMMA reloc_16bit { + sect_ConstByte(0x01 | ($2 << 4)); sect_RelWord($4, 1); } - | SM83_LD MODE_DE COMMA reloc_16bit { - sect_ConstByte(0x01 | (REG_DE << 4)); - sect_RelWord($4, 1); + | SM83_LD reg_bc_or_de COMMA reg_tt_no_af { + ::error( + "LD %s, %s is not a valid instruction; use LD %s, %s and LD %s, %s\n", + reg_tt_names[$2], + reg_tt_names[$4], + reg_tt_high_names[$2], + reg_tt_high_names[$4], + reg_tt_low_names[$2], + reg_tt_low_names[$4] + ); } // HL is taken care of in sm83_ld_hl // SP is taken care of in sm83_ld_sp @@ -2532,33 +2559,33 @@ reg_a: ; reg_tt: - MODE_BC { - $$ = REG_BC; - } - | MODE_DE { - $$ = REG_DE; - } - | MODE_HL { - $$ = REG_HL; - } + reg_tt_no_af | MODE_AF { $$ = REG_AF; } ; reg_ss: + reg_tt_no_af + | MODE_SP { + $$ = REG_SP; + } +; + +reg_tt_no_af: + reg_bc_or_de + | MODE_HL { + $$ = REG_HL; + } +; + +reg_bc_or_de: MODE_BC { $$ = REG_BC; } | MODE_DE { $$ = REG_DE; } - | MODE_HL { - $$ = REG_HL; - } - | MODE_SP { - $$ = REG_SP; - } ; reg_rr: diff --git a/src/asm/rpn.cpp b/src/asm/rpn.cpp index 36009431..865816e7 100644 --- a/src/asm/rpn.cpp +++ b/src/asm/rpn.cpp @@ -582,7 +582,7 @@ void Expression::makeCheckBitIndex(uint8_t mask) { // Checks that an RPN expression's value fits within N bits (signed or unsigned) void Expression::checkNBit(uint8_t n) const { if (isKnown()) { - ::checkNBit(value(), n, "Expression"); + ::checkNBit(value(), n, nullptr); } } @@ -591,11 +591,23 @@ bool checkNBit(int32_t v, uint8_t n, char const *name) { assume(n < CHAR_BIT * sizeof(int)); // Otherwise `1 << n` is UB if (v < -(1 << n) || v >= 1 << n) { - warning(WARNING_TRUNCATION_1, "%s must be %u-bit\n", name, n); + warning( + WARNING_TRUNCATION_1, + n == 8 && !name ? "%s must be %u-bit; use LOW() to force 8-bit\n" + : "%s must be %u-bit\n", + name ? name : "Expression", + n + ); return false; } if (v < -(1 << (n - 1))) { - warning(WARNING_TRUNCATION_2, "%s must be %u-bit\n", name, n); + warning( + WARNING_TRUNCATION_2, + n == 8 && !name ? "%s must be %u-bit; use LOW() to force 8-bit\n" + : "%s must be %u-bit\n", + name ? name : "Expression", + n + ); return false; } diff --git a/src/bison.sh b/src/bison.sh index 41a635df..3a18d076 100755 --- a/src/bison.sh +++ b/src/bison.sh @@ -15,14 +15,17 @@ fi BISON_FLAGS="-Wall -Dparse.lac=full -Dlr.type=ielr" # Set some optimization flags on versions that support them -if [ "$BISON_MAJOR" -eq 4 ] || [ "$BISON_MAJOR" -eq 3 ] && [ "$BISON_MINOR" -ge 5 ]; then +if [ "$BISON_MAJOR" -ge 4 ] || [ "$BISON_MAJOR" -eq 3 ] && [ "$BISON_MINOR" -ge 5 ]; then BISON_FLAGS="$BISON_FLAGS -Dapi.token.raw=true" fi -if [ "$BISON_MAJOR" -eq 4 ] || [ "$BISON_MAJOR" -eq 3 ] && [ "$BISON_MINOR" -ge 6 ]; then +if [ "$BISON_MAJOR" -ge 4 ] || [ "$BISON_MAJOR" -eq 3 ] && [ "$BISON_MINOR" -ge 6 ]; then BISON_FLAGS="$BISON_FLAGS -Dparse.error=detailed" else BISON_FLAGS="$BISON_FLAGS -Dparse.error=verbose" fi +if [ "$BISON_MAJOR" -ge 4 ] || [ "$BISON_MAJOR" -eq 3 ] && [ "$BISON_MINOR" -ge 7 ]; then + BISON_FLAGS="$BISON_FLAGS -Wcounterexamples" +fi # Replace the arguments to this script ($@) with the ones in $BISON_FLAGS eval "set -- $BISON_FLAGS" diff --git a/test/asm/ds-bad.err b/test/asm/ds-bad.err index e573845a..dba29b84 100644 --- a/test/asm/ds-bad.err +++ b/test/asm/ds-bad.err @@ -1,5 +1,5 @@ error: ds-bad.asm(3): Expected constant expression: 'unknown' is not constant at assembly time warning: ds-bad.asm(4): [-Wtruncation] - Expression must be 8-bit + Expression must be 8-bit; use LOW() to force 8-bit error: Assembly aborted (1 error)! diff --git a/test/asm/invalid-instructions.asm b/test/asm/invalid-instructions.asm index 3ed9580f..eedf4a4c 100644 --- a/test/asm/invalid-instructions.asm +++ b/test/asm/invalid-instructions.asm @@ -6,3 +6,16 @@ SECTION "invalid", ROM0[$10000] ld b, [$4000] bit 8, a rst $40 + + ld bc, bc + ld de, hl + ld hl, de + + ld hl, sp ; no offset! + ; ld sp, hl is valid + + ld sp, bc + ld bc, sp + + ld af, bc + ld bc, af diff --git a/test/asm/invalid-instructions.err b/test/asm/invalid-instructions.err index 7a903a7d..efc6232a 100644 --- a/test/asm/invalid-instructions.err +++ b/test/asm/invalid-instructions.err @@ -16,4 +16,20 @@ error: invalid-instructions.asm(7): Invalid bit index 8 for BIT error: invalid-instructions.asm(8): Invalid address $40 for RST -error: Assembly aborted (8 errors)! +error: invalid-instructions.asm(10): + LD BC, BC is not a valid instruction; use LD B, B and LD C, C +error: invalid-instructions.asm(11): + LD DE, HL is not a valid instruction; use LD D, H and LD E, L +error: invalid-instructions.asm(12): + LD HL, DE is not a valid instruction; use LD H, D and LD L, E +error: invalid-instructions.asm(14): + LD HL, SP is not a valid instruction; use LD HL, SP + 0 +error: invalid-instructions.asm(17): + LD SP, BC is not a valid instruction +error: invalid-instructions.asm(18): + syntax error, unexpected sp +error: invalid-instructions.asm(20): + syntax error, unexpected af +error: invalid-instructions.asm(21): + syntax error, unexpected af +error: Assembly aborted (16 errors)! diff --git a/test/asm/warn-truncation.err b/test/asm/warn-truncation.err index 37e4c791..b3a8a36d 100644 --- a/test/asm/warn-truncation.err +++ b/test/asm/warn-truncation.err @@ -1,64 +1,64 @@ warning: warn-truncation.asm(35) -> warn-truncation.asm::try(23): [-Wtruncation] - Expression must be 8-bit + Expression must be 8-bit; use LOW() to force 8-bit warning: warn-truncation.asm(35) -> warn-truncation.asm::try(24): [-Wtruncation] - Expression must be 8-bit + Expression must be 8-bit; use LOW() to force 8-bit warning: warn-truncation.asm(35) -> warn-truncation.asm::try(25): [-Wtruncation] Expression must be 16-bit warning: warn-truncation.asm(35) -> warn-truncation.asm::try(26): [-Wtruncation] Expression must be 16-bit warning: warn-truncation.asm(35) -> warn-truncation.asm::try(28): [-Wtruncation] - Expression must be 8-bit + Expression must be 8-bit; use LOW() to force 8-bit warning: warn-truncation.asm(35) -> warn-truncation.asm::try(29): [-Wtruncation] Expression must be 16-bit warning: warn-truncation.asm(35) -> warn-truncation.asm::try(30): [-Wtruncation] - Expression must be 8-bit + Expression must be 8-bit; use LOW() to force 8-bit warning: warn-truncation.asm(35) -> warn-truncation.asm::try(31): [-Wtruncation] Expression must be 16-bit warning: warn-truncation.asm(37) -> warn-truncation.asm::try(23): [-Wtruncation] - Expression must be 8-bit + Expression must be 8-bit; use LOW() to force 8-bit warning: warn-truncation.asm(37) -> warn-truncation.asm::try(24): [-Wtruncation] - Expression must be 8-bit + Expression must be 8-bit; use LOW() to force 8-bit warning: warn-truncation.asm(37) -> warn-truncation.asm::try(25): [-Wtruncation] Expression must be 16-bit warning: warn-truncation.asm(37) -> warn-truncation.asm::try(26): [-Wtruncation] Expression must be 16-bit warning: warn-truncation.asm(38) -> warn-truncation.asm::try(23): [-Wtruncation] - Expression must be 8-bit + Expression must be 8-bit; use LOW() to force 8-bit warning: warn-truncation.asm(38) -> warn-truncation.asm::try(24): [-Wtruncation] - Expression must be 8-bit + Expression must be 8-bit; use LOW() to force 8-bit warning: warn-truncation.asm(38) -> warn-truncation.asm::try(25): [-Wtruncation] Expression must be 16-bit warning: warn-truncation.asm(38) -> warn-truncation.asm::try(26): [-Wtruncation] Expression must be 16-bit warning: warn-truncation.asm(38) -> warn-truncation.asm::try(28): [-Wtruncation] - Expression must be 8-bit + Expression must be 8-bit; use LOW() to force 8-bit warning: warn-truncation.asm(38) -> warn-truncation.asm::try(29): [-Wtruncation] Expression must be 16-bit warning: warn-truncation.asm(38) -> warn-truncation.asm::try(30): [-Wtruncation] - Expression must be 8-bit + Expression must be 8-bit; use LOW() to force 8-bit warning: warn-truncation.asm(38) -> warn-truncation.asm::try(31): [-Wtruncation] Expression must be 16-bit error: warn-truncation.asm(39) -> warn-truncation.asm::try(23): [-Werror=truncation] - Expression must be 8-bit + Expression must be 8-bit; use LOW() to force 8-bit error: warn-truncation.asm(39) -> warn-truncation.asm::try(24): [-Werror=truncation] - Expression must be 8-bit + Expression must be 8-bit; use LOW() to force 8-bit error: warn-truncation.asm(39) -> warn-truncation.asm::try(25): [-Werror=truncation] Expression must be 16-bit error: warn-truncation.asm(39) -> warn-truncation.asm::try(26): [-Werror=truncation] Expression must be 16-bit error: warn-truncation.asm(40) -> warn-truncation.asm::try(23): [-Werror=truncation] - Expression must be 8-bit + Expression must be 8-bit; use LOW() to force 8-bit error: warn-truncation.asm(40) -> warn-truncation.asm::try(24): [-Werror=truncation] - Expression must be 8-bit + Expression must be 8-bit; use LOW() to force 8-bit error: warn-truncation.asm(40) -> warn-truncation.asm::try(25): [-Werror=truncation] Expression must be 16-bit error: warn-truncation.asm(40) -> warn-truncation.asm::try(26): [-Werror=truncation] Expression must be 16-bit error: warn-truncation.asm(40) -> warn-truncation.asm::try(28): [-Werror=truncation] - Expression must be 8-bit + Expression must be 8-bit; use LOW() to force 8-bit error: warn-truncation.asm(40) -> warn-truncation.asm::try(29): [-Werror=truncation] Expression must be 16-bit error: warn-truncation.asm(40) -> warn-truncation.asm::try(30): [-Werror=truncation] - Expression must be 8-bit + Expression must be 8-bit; use LOW() to force 8-bit error: warn-truncation.asm(40) -> warn-truncation.asm::try(31): [-Werror=truncation] Expression must be 16-bit