diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 6d3c83a404..3f55c8be4d 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -5722,8 +5722,44 @@ static void NewSpriteGroup(ByteReader *buf) } else { switch (adjust.operation) { case DSGA_OP_SMIN: + if (adjust.variable == 0x1A && adjust.shift_num == 0 && adjust.and_mask == 1) { + DeterministicSpriteGroupAdjust &prev = group->adjusts[group->adjusts.size() - 2]; + if (prev.operation == DSGA_OP_SCMP) { + prev.operation = DSGA_OP_SGE; + group->adjusts.pop_back(); + inference = VA2AIF_SIGNED_NON_NEGATIVE | VA2AIF_ONE_OR_ZERO; + break; + } + if (group->adjusts.size() >= 3 && prev.operation == DSGA_OP_XOR && prev.type == DSGA_TYPE_NONE && prev.variable == 0x1A && + prev.shift_num == 0 && prev.and_mask == 2) { + DeterministicSpriteGroupAdjust &prev2 = group->adjusts[group->adjusts.size() - 3]; + if (prev2.operation == DSGA_OP_SCMP) { + prev2.operation = DSGA_OP_SLE; + group->adjusts.pop_back(); + group->adjusts.pop_back(); + inference = VA2AIF_SIGNED_NON_NEGATIVE | VA2AIF_ONE_OR_ZERO; + break; + } + } + } if (adjust.and_mask <= 1 && (prev_inference & VA2AIF_SIGNED_NON_NEGATIVE)) inference = VA2AIF_SIGNED_NON_NEGATIVE | VA2AIF_ONE_OR_ZERO; break; + case DSGA_OP_SMAX: + if (adjust.variable == 0x1A && adjust.shift_num == 0 && adjust.and_mask == 0) { + DeterministicSpriteGroupAdjust &prev = group->adjusts[group->adjusts.size() - 2]; + if (group->adjusts.size() >= 3 && prev.operation == DSGA_OP_SUB && prev.type == DSGA_TYPE_NONE && prev.variable == 0x1A && + prev.shift_num == 0 && prev.and_mask == 1) { + DeterministicSpriteGroupAdjust &prev2 = group->adjusts[group->adjusts.size() - 3]; + if (prev2.operation == DSGA_OP_SCMP) { + prev2.operation = DSGA_OP_SGT; + group->adjusts.pop_back(); + group->adjusts.pop_back(); + inference = VA2AIF_SIGNED_NON_NEGATIVE | VA2AIF_ONE_OR_ZERO; + break; + } + } + } + break; case DSGA_OP_UMIN: if (adjust.and_mask <= 1) inference = VA2AIF_SIGNED_NON_NEGATIVE | VA2AIF_ONE_OR_ZERO; break; @@ -5737,7 +5773,7 @@ static void NewSpriteGroup(ByteReader *buf) group->adjusts.pop_back(); break; } - if ((prev_inference & VA2AIF_SIGNED_NON_NEGATIVE) && adjust.variable == 0x1A && adjust.shift_num == 0 && adjust.and_mask == 1) { + if (adjust.variable == 0x1A && adjust.shift_num == 0 && adjust.and_mask == 1) { DeterministicSpriteGroupAdjust &prev = group->adjusts[group->adjusts.size() - 2]; if (prev.operation == DSGA_OP_SCMP || prev.operation == DSGA_OP_UCMP) { prev.operation = DSGA_OP_EQ; @@ -5753,7 +5789,18 @@ static void NewSpriteGroup(ByteReader *buf) } break; case DSGA_OP_OR: + if (adjust.and_mask <= 1) inference = prev_inference & (VA2AIF_SIGNED_NON_NEGATIVE | VA2AIF_ONE_OR_ZERO); + break; case DSGA_OP_XOR: + if (adjust.variable == 0x1A && adjust.shift_num == 0 && adjust.and_mask == 1) { + DeterministicSpriteGroupAdjust &prev = group->adjusts[group->adjusts.size() - 2]; + if (prev.operation == DSGA_OP_SLT || prev.operation == DSGA_OP_SGE || prev.operation == DSGA_OP_SLE || prev.operation == DSGA_OP_SGT) { + prev.operation = (DeterministicSpriteGroupAdjustOperation)(prev.operation ^ 1); + group->adjusts.pop_back(); + inference = VA2AIF_SIGNED_NON_NEGATIVE | VA2AIF_ONE_OR_ZERO; + break; + } + } if (adjust.and_mask <= 1) inference = prev_inference & (VA2AIF_SIGNED_NON_NEGATIVE | VA2AIF_ONE_OR_ZERO); break; case DSGA_OP_MUL: { @@ -5804,12 +5851,6 @@ static void NewSpriteGroup(ByteReader *buf) break; } case DSGA_OP_SCMP: - inference = VA2AIF_SIGNED_NON_NEGATIVE; - /* Convert to UCMP if possible to make other analysis operations easier */ - if ((prev_inference & VA2AIF_SIGNED_NON_NEGATIVE) && adjust.variable == 0x1A && adjust.shift_num == 0 && (adjust.and_mask & get_sign_bit()) == 0) { - adjust.operation = DSGA_OP_UCMP; - } - break; case DSGA_OP_UCMP: inference = VA2AIF_SIGNED_NON_NEGATIVE; break; diff --git a/src/newgrf_spritegroup.cpp b/src/newgrf_spritegroup.cpp index d94670ec45..5613aca3fd 100644 --- a/src/newgrf_spritegroup.cpp +++ b/src/newgrf_spritegroup.cpp @@ -184,6 +184,10 @@ static U EvalAdjustT(const DeterministicSpriteGroupAdjust &adjust, ScopeResolver case DSGA_OP_SAR: return (int32)(S)last_value >> ((U)value & 0x1F); case DSGA_OP_TERNARY: return (last_value != 0) ? value : adjust.add_val; case DSGA_OP_EQ: return (last_value == value) ? 1 : 0; + case DSGA_OP_SLT: return ((S)last_value < (S)value) ? 1 : 0; + case DSGA_OP_SGE: return ((S)last_value >= (S)value) ? 1 : 0; + case DSGA_OP_SLE: return ((S)last_value <= (S)value) ? 1 : 0; + case DSGA_OP_SGT: return ((S)last_value > (S)value) ? 1 : 0; default: return value; } } @@ -544,6 +548,16 @@ static const char *_dsg_op_names[] { }; static_assert(lengthof(_dsg_op_names) == DSGA_OP_END); +static const char *_dsg_op_special_names[] { + "TERNARY", + "EQ", + "SLT", + "SGE", + "SLE", + "SGT", +}; +static_assert(lengthof(_dsg_op_special_names) == DSGA_OP_SPECIAL_END - DSGA_OP_TERNARY); + static const char *_sg_scope_names[] { "SELF", "PARENT", @@ -560,8 +574,7 @@ static const char *_sg_size_names[] { static const char *GetAdjustOperationName(DeterministicSpriteGroupAdjustOperation operation) { if (operation < DSGA_OP_END) return _dsg_op_names[operation]; - if (operation == DSGA_OP_TERNARY) return "TERNARY"; - if (operation == DSGA_OP_EQ) return "EQ"; + if (operation >= DSGA_OP_TERNARY && operation < DSGA_OP_SPECIAL_END) return _dsg_op_special_names[operation - DSGA_OP_TERNARY]; return "???"; } diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h index 316157af7c..583d3446c5 100644 --- a/src/newgrf_spritegroup.h +++ b/src/newgrf_spritegroup.h @@ -164,10 +164,17 @@ enum DeterministicSpriteGroupAdjustOperation { DSGA_OP_TERNARY = 0x80, ///< a == 0 ? b : c, DSGA_OP_EQ, ///< a == b ? 1 : 0, + DSGA_OP_SLT, ///< (signed) a < b ? 1 : 0, + DSGA_OP_SGE, ///< (signed) a >= b ? 1 : 0, + DSGA_OP_SLE, ///< (signed) a <= b ? 1 : 0, + DSGA_OP_SGT, ///< (signed) a > b ? 1 : 0, DSGA_OP_SPECIAL_END, }; +static_assert((DSGA_OP_SLT ^ 1) == DSGA_OP_SGE); +static_assert((DSGA_OP_SLE ^ 1) == DSGA_OP_SGT); + inline bool IsEvalAdjustWithZeroRemovable(DeterministicSpriteGroupAdjustOperation op) { switch (op) {