1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-02-18 01:30:56 +00:00

Add various insurances against undefined behaviour.

This commit is contained in:
Thomas Harte 2022-10-18 11:30:40 -04:00
parent a364499d17
commit a1ae7c28b2

View File

@ -325,7 +325,7 @@ template <Operation operation, typename IntT, typename FlowController> void shif
operation == Operation::LSRb || operation == Operation::LSRw || operation == Operation::LSRl operation == Operation::LSRb || operation == Operation::LSRw || operation == Operation::LSRl
); );
const auto size = bit_size<IntT>(); constexpr auto size = bit_size<IntT>();
const auto shift = shift_count<IntT>(uint8_t(source), flow_controller); const auto shift = shift_count<IntT>(uint8_t(source), flow_controller);
if(!shift) { if(!shift) {
@ -352,30 +352,46 @@ template <Operation operation, typename IntT, typename FlowController> void shif
switch(type) { switch(type) {
case Type::ASL: case Type::ASL:
case Type::LSL: case Type::LSL:
status.overflow_flag = if(shift > size) {
type == Type::LSL ?
0 : (destination ^ (destination << shift)) & top_bit<IntT>();
if(shift < size) {
status.carry_flag = status.extend_flag = (destination >> (size - shift)) & 1;
} else {
status.carry_flag = status.extend_flag = 0; status.carry_flag = status.extend_flag = 0;
} else {
status.carry_flag = status.extend_flag = (destination << (shift - 1)) & top_bit<IntT>();
}
if(type == Type::LSL) {
status.overflow_flag = 0;
} else {
if(shift >= size) {
status.overflow_flag = destination & top_bit<IntT>();
} else {
status.overflow_flag = (destination ^ (destination << shift)) & top_bit<IntT>();
}
}
if(shift >= size) {
destination = 0;
} else {
destination <<= shift;
} }
destination <<= shift;
break; break;
case Type::ASR: case Type::ASR:
case Type::LSR: { case Type::LSR: {
if(shift > size) {
status.carry_flag = status.extend_flag = 0;
} else {
status.carry_flag = status.extend_flag = (destination >> (shift - 1)) & 1;
}
status.overflow_flag = 0;
const IntT sign_word = const IntT sign_word =
type == Type::LSR ? type == Type::LSR ?
0 : (destination & top_bit<IntT>() ? IntT(~0) : 0); 0 : (destination & top_bit<IntT>() ? IntT(~0) : 0);
status.overflow_flag = 0; if(shift >= size) {
status.carry_flag = status.extend_flag = (destination >> (shift - 1)) & 1;
if(shift < size) {
destination = IntT((destination >> shift) | (sign_word << (size - shift)));
} else {
destination = sign_word; destination = sign_word;
} else {
destination = IntT((destination >> shift) | (sign_word << (size - shift)));
} }
} break; } break;
} }
@ -391,7 +407,7 @@ template <Operation operation, typename IntT, typename FlowController> void rota
operation == Operation::RORb || operation == Operation::RORw || operation == Operation::RORl operation == Operation::RORb || operation == Operation::RORw || operation == Operation::RORl
); );
const auto size = bit_size<IntT>(); constexpr auto size = bit_size<IntT>();
auto shift = shift_count<IntT>(uint8_t(source), flow_controller); auto shift = shift_count<IntT>(uint8_t(source), flow_controller);
if(!shift) { if(!shift) {
@ -401,17 +417,21 @@ template <Operation operation, typename IntT, typename FlowController> void rota
switch(operation) { switch(operation) {
case Operation::ROLb: case Operation::ROLw: case Operation::ROLl: case Operation::ROLb: case Operation::ROLw: case Operation::ROLl:
destination = IntT( if(shift) {
(destination << shift) | destination = IntT(
(destination >> (size - shift)) (destination << shift) |
); (destination >> (size - shift))
);
}
status.carry_flag = Status::FlagT(destination & 1); status.carry_flag = Status::FlagT(destination & 1);
break; break;
case Operation::RORb: case Operation::RORw: case Operation::RORl: case Operation::RORb: case Operation::RORw: case Operation::RORl:
destination = IntT( if(shift) {
(destination >> shift) | destination = IntT(
(destination << (size - shift)) (destination >> shift) |
); (destination << (size - shift))
);
}
status.carry_flag = Status::FlagT(destination & top_bit<IntT>()); status.carry_flag = Status::FlagT(destination & top_bit<IntT>());
break; break;
} }
@ -428,37 +448,56 @@ template <Operation operation, typename IntT, typename FlowController> void rox(
operation == Operation::ROXRb || operation == Operation::ROXRw || operation == Operation::ROXRl operation == Operation::ROXRb || operation == Operation::ROXRw || operation == Operation::ROXRl
); );
const auto size = bit_size<IntT>(); constexpr auto size = bit_size<IntT>();
auto shift = shift_count<IntT>(uint8_t(source), flow_controller) % (size + 1); auto shift = shift_count<IntT>(uint8_t(source), flow_controller) % (size + 1);
if(!shift) { if(!shift) {
// When shift is zero, extend is unaffected but is copied to carry. // When shift is zero, extend is unaffected but is copied to carry.
status.carry_flag = status.extend_flag; status.carry_flag = status.extend_flag;
} else { } else {
// TODO: if the value of the right operand is negative or is greater or equal to
// the number of bits in the promoted left operand, the behavior is undefined.
//
// i.e. for a long if shift >= 32,
// or size + 1 - shift >= 32, i.e. 1 - shift >= 0, 1 >= shift; i.e. shift <= 1
// ... the code below is undefined behaviour.
switch(operation) { switch(operation) {
case Operation::ROXLb: case Operation::ROXLw: case Operation::ROXLl: case Operation::ROXLb: case Operation::ROXLw: case Operation::ROXLl:
status.carry_flag = Status::FlagT((destination >> (size - shift)) & 1); status.carry_flag = Status::FlagT((destination >> (size - shift)) & 1);
destination = IntT(
(destination << shift) | if(shift == bit_size<IntT>()) {
(IntT(status.extend_flag ? 1 : 0) << (shift - 1)) | destination = IntT(
(destination >> (size + 1 - shift)) (status.extend_flag ? top_bit<IntT>() : 0) |
); (destination >> 1)
);
} else if(shift == 1) {
destination = IntT(
(destination << 1) |
IntT(status.extend_flag ? 1 : 0)
);
} else {
destination = IntT(
(destination << shift) |
(IntT(status.extend_flag ? 1 : 0) << (shift - 1)) |
(destination >> (size + 1 - shift))
);
}
status.extend_flag = status.carry_flag; status.extend_flag = status.carry_flag;
break; break;
case Operation::ROXRb: case Operation::ROXRw: case Operation::ROXRl: case Operation::ROXRb: case Operation::ROXRw: case Operation::ROXRl:
status.carry_flag = Status::FlagT(destination & (1 << (shift - 1))); status.carry_flag = Status::FlagT(destination & (1 << (shift - 1)));
destination = IntT(
(destination >> shift) | if(shift == bit_size<IntT>()) {
((status.extend_flag ? top_bit<IntT>() : 0) >> (shift - 1)) | destination = IntT(
(destination << (size + 1 - shift)) (status.extend_flag ? 1 : 0) |
); (destination << 1)
);
} else if(shift == 1) {
destination = IntT(
(destination >> 1) |
(status.extend_flag ? top_bit<IntT>() : 0)
);
} else {
destination = IntT(
(destination >> shift) |
((status.extend_flag ? top_bit<IntT>() : 0) >> (shift - 1)) |
(destination << (size + 1 - shift))
);
}
status.extend_flag = status.carry_flag; status.extend_flag = status.carry_flag;
break; break;
} }