mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-11 08:30:55 +00:00
Ensure that reads can only read, accept that source is sometimes written to. E.g. XCHG.
This commit is contained in:
parent
02af08ffd2
commit
1d479ec2d7
@ -138,7 +138,7 @@ std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(con
|
||||
|
||||
PartialBlock(0x00, ADD); break;
|
||||
case 0x06: Complete(PUSH, ES, None, data_size_); break;
|
||||
case 0x07: Complete(POP, ES, None, data_size_); break;
|
||||
case 0x07: Complete(POP, None, ES, data_size_); break;
|
||||
|
||||
PartialBlock(0x08, OR); break;
|
||||
case 0x0e: Complete(PUSH, CS, None, data_size_); break;
|
||||
@ -147,7 +147,7 @@ std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(con
|
||||
// prefixed with $0f.
|
||||
case 0x0f:
|
||||
if constexpr (model < Model::i80286) {
|
||||
Complete(POP, CS, None, data_size_);
|
||||
Complete(POP, None, CS, data_size_);
|
||||
} else {
|
||||
phase_ = Phase::InstructionPageF;
|
||||
}
|
||||
@ -155,11 +155,11 @@ std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(con
|
||||
|
||||
PartialBlock(0x10, ADC); break;
|
||||
case 0x16: Complete(PUSH, SS, None, DataSize::Word); break;
|
||||
case 0x17: Complete(POP, SS, None, DataSize::Word); break;
|
||||
case 0x17: Complete(POP, None, SS, DataSize::Word); break;
|
||||
|
||||
PartialBlock(0x18, SBB); break;
|
||||
case 0x1e: Complete(PUSH, DS, None, DataSize::Word); break;
|
||||
case 0x1f: Complete(POP, DS, None, DataSize::Word); break;
|
||||
case 0x1f: Complete(POP, None, DS, DataSize::Word); break;
|
||||
|
||||
PartialBlock(0x20, AND); break;
|
||||
case 0x26: segment_override_ = Source::ES; break;
|
||||
@ -523,6 +523,7 @@ std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(con
|
||||
|
||||
// MARK: - Additional F page of instructions.
|
||||
|
||||
if constexpr (model >= Model::i80286) {
|
||||
if(phase_ == Phase::InstructionPageF && source != end) {
|
||||
// Update the instruction acquired.
|
||||
const uint8_t instr = *source;
|
||||
@ -610,7 +611,7 @@ std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(con
|
||||
#undef Set
|
||||
|
||||
case 0xa0: RequiresMin(i80386); Complete(PUSH, FS, None, data_size_); break;
|
||||
case 0xa1: RequiresMin(i80386); Complete(POP, FS, None, data_size_); break;
|
||||
case 0xa1: RequiresMin(i80386); Complete(POP, None, FS, data_size_); break;
|
||||
case 0xa3: RequiresMin(i80386); MemRegReg(BT, MemReg_Reg, data_size_); break;
|
||||
case 0xa4:
|
||||
RequiresMin(i80386);
|
||||
@ -622,7 +623,7 @@ std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(con
|
||||
MemRegReg(SHLDCL, MemReg_Reg, data_size_);
|
||||
break;
|
||||
case 0xa8: RequiresMin(i80386); Complete(PUSH, GS, None, data_size_); break;
|
||||
case 0xa9: RequiresMin(i80386); Complete(POP, GS, None, data_size_); break;
|
||||
case 0xa9: RequiresMin(i80386); Complete(POP, None, GS, data_size_); break;
|
||||
case 0xab: RequiresMin(i80386); MemRegReg(BTS, MemReg_Reg, data_size_); break;
|
||||
case 0xac:
|
||||
RequiresMin(i80386);
|
||||
@ -664,6 +665,7 @@ std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(con
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef Requires
|
||||
#undef RequiresMin
|
||||
@ -979,6 +981,7 @@ std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(con
|
||||
|
||||
// MARK: - ScaleIndexBase
|
||||
|
||||
if constexpr (is_32bit(model)) {
|
||||
if(phase_ == Phase::ScaleIndexBase && source != end) {
|
||||
sib_ = *source;
|
||||
++source;
|
||||
@ -992,6 +995,7 @@ std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(con
|
||||
|
||||
phase_ = (displacement_size_ != DataSize::None || operand_size_ != DataSize::None) ? Phase::DisplacementOrOperand : Phase::ReadyToPost;
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Displacement and operand.
|
||||
|
||||
@ -1041,6 +1045,18 @@ std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(con
|
||||
// MARK: - Check for completion.
|
||||
|
||||
if(phase_ == Phase::ReadyToPost) {
|
||||
// TODO: map to #UD where applicable; build LOCK into the Operation type, buying an extra bit for the operation?
|
||||
//
|
||||
// As of the P6 Intel stipulates that:
|
||||
//
|
||||
// "The LOCK prefix can be prepended only to the following instructions and to those forms of the instructions
|
||||
// that use a memory operand: ADD, ADC, AND, BTC, BTR, BTS, CMPXCHG, DEC, INC, NEG, NOT, OR, SBB, SUB, XOR,
|
||||
// XADD, and XCHG."
|
||||
//
|
||||
// ... and the #UD exception will be raised if LOCK is encountered elsewhere. So adding 17 additional
|
||||
// operations would unlock an extra bit of storage for a net gain of 239 extra operation types and thereby
|
||||
// alleviating any concerns over whether there'll be space to handle MMX, floating point extensions, etc.
|
||||
|
||||
const auto result = std::make_pair(
|
||||
consumed_,
|
||||
InstructionT(
|
||||
|
@ -1523,9 +1523,14 @@ template <
|
||||
using IntT = typename DataSizeType<data_size>::type;
|
||||
using AddressT = typename AddressSizeType<address_size>::type;
|
||||
|
||||
// Establish source() and destination() shorthand to fetch data if necessary.
|
||||
// Establish source() and destination() shorthands to fetch data if necessary.
|
||||
//
|
||||
// C++17, which this project targets at the time of writing, does not provide templatised lambdas.
|
||||
// So the following division is in part a necessity.
|
||||
//
|
||||
// (though GCC offers C++20 syntax as an extension, and Clang seems to follow along, so maybe I'm overthinking)
|
||||
IntT immediate;
|
||||
const auto source = [&]() -> IntT& {
|
||||
const auto source_r = [&]() -> IntT& {
|
||||
return *resolve<model, IntT, AccessType::Read>(
|
||||
instruction,
|
||||
instruction.source().source(),
|
||||
@ -1535,11 +1540,16 @@ template <
|
||||
nullptr,
|
||||
&immediate);
|
||||
};
|
||||
|
||||
// C++17, which this project targets at the time of writing, does not provide templatised lambdas.
|
||||
// So the following division is in part a necessity.
|
||||
//
|
||||
// (though GCC offers C++20 syntax as an extension, and Clang seems to follow along, so maybe I'm overthinking)
|
||||
const auto source_rmw = [&]() -> IntT& {
|
||||
return *resolve<model, IntT, AccessType::ReadModifyWrite>(
|
||||
instruction,
|
||||
instruction.source().source(),
|
||||
instruction.source(),
|
||||
registers,
|
||||
memory,
|
||||
nullptr,
|
||||
&immediate);
|
||||
};
|
||||
const auto destination_r = [&]() -> IntT& {
|
||||
return *resolve<model, IntT, AccessType::Read>(
|
||||
instruction,
|
||||
@ -1661,26 +1671,26 @@ template <
|
||||
case Operation::HLT: flow_controller.halt(); return;
|
||||
case Operation::WAIT: flow_controller.wait(); return;
|
||||
|
||||
case Operation::ADC: Primitive::add<true>(destination_rmw(), source(), status); break;
|
||||
case Operation::ADD: Primitive::add<false>(destination_rmw(), source(), status); break;
|
||||
case Operation::SBB: Primitive::sub<true, true>(destination_rmw(), source(), status); break;
|
||||
case Operation::SUB: Primitive::sub<false, true>(destination_rmw(), source(), status); break;
|
||||
case Operation::CMP: Primitive::sub<false, false>(destination_rmw(), source(), status); break;
|
||||
case Operation::TEST: Primitive::test(destination_r(), source(), status); return;
|
||||
case Operation::ADC: Primitive::add<true>(destination_rmw(), source_r(), status); break;
|
||||
case Operation::ADD: Primitive::add<false>(destination_rmw(), source_r(), status); break;
|
||||
case Operation::SBB: Primitive::sub<true, true>(destination_rmw(), source_r(), status); break;
|
||||
case Operation::SUB: Primitive::sub<false, true>(destination_rmw(), source_r(), status); break;
|
||||
case Operation::CMP: Primitive::sub<false, false>(destination_rmw(), source_r(), status); return;
|
||||
case Operation::TEST: Primitive::test(destination_r(), source_r(), status); return;
|
||||
|
||||
case Operation::MUL: Primitive::mul(pair_high(), pair_low(), source(), status); return;
|
||||
case Operation::IMUL_1: Primitive::imul(pair_high(), pair_low(), source(), status); return;
|
||||
case Operation::DIV: Primitive::div(pair_high(), pair_low(), source(), flow_controller); return;
|
||||
case Operation::IDIV: Primitive::idiv(pair_high(), pair_low(), source(), flow_controller); return;
|
||||
case Operation::MUL: Primitive::mul(pair_high(), pair_low(), source_r(), status); return;
|
||||
case Operation::IMUL_1: Primitive::imul(pair_high(), pair_low(), source_r(), status); return;
|
||||
case Operation::DIV: Primitive::div(pair_high(), pair_low(), source_r(), flow_controller); return;
|
||||
case Operation::IDIV: Primitive::idiv(pair_high(), pair_low(), source_r(), flow_controller); return;
|
||||
|
||||
case Operation::INC: Primitive::inc(destination_rmw(), status); break;
|
||||
case Operation::DEC: Primitive::dec(destination_rmw(), status); break;
|
||||
|
||||
case Operation::AND: Primitive::and_(destination_rmw(), source(), status); break;
|
||||
case Operation::OR: Primitive::or_(destination_rmw(), source(), status); break;
|
||||
case Operation::XOR: Primitive::xor_(destination_rmw(), source(), status); break;
|
||||
case Operation::NEG: Primitive::neg(source(), status); break; // TODO: should be a destination.
|
||||
case Operation::NOT: Primitive::not_(source()); break; // TODO: should be a destination.
|
||||
case Operation::AND: Primitive::and_(destination_rmw(), source_r(), status); break;
|
||||
case Operation::OR: Primitive::or_(destination_rmw(), source_r(), status); break;
|
||||
case Operation::XOR: Primitive::xor_(destination_rmw(), source_r(), status); break;
|
||||
case Operation::NEG: Primitive::neg(source_rmw(), status); break; // TODO: should be a destination.
|
||||
case Operation::NOT: Primitive::not_(source_rmw()); break; // TODO: should be a destination.
|
||||
|
||||
case Operation::CALLrel:
|
||||
Primitive::call_relative(instruction.displacement(), registers, flow_controller);
|
||||
@ -1715,7 +1725,7 @@ template <
|
||||
case Operation::LES: if constexpr (data_size == DataSize::Word) Primitive::ld<model, Source::ES>(instruction, destination_w(), memory, registers); return;
|
||||
|
||||
case Operation::LEA: Primitive::lea<model>(instruction, destination_w(), memory, registers); return;
|
||||
case Operation::MOV: Primitive::mov(destination_w(), source()); break;
|
||||
case Operation::MOV: Primitive::mov(destination_w(), source_r()); break;
|
||||
|
||||
case Operation::JO: jcc(status.condition<Condition::Overflow>()); return;
|
||||
case Operation::JNO: jcc(!status.condition<Condition::Overflow>()); return;
|
||||
@ -1750,7 +1760,7 @@ template <
|
||||
case Operation::STI: Primitive::sti(status); return;
|
||||
case Operation::CMC: Primitive::cmc(status); return;
|
||||
|
||||
case Operation::XCHG: Primitive::xchg(destination_rmw(), source()); break;
|
||||
case Operation::XCHG: Primitive::xchg(destination_rmw(), source_rmw()); break;
|
||||
|
||||
case Operation::SALC: Primitive::salc(registers.al(), status); return;
|
||||
case Operation::SETMO:
|
||||
@ -1775,8 +1785,8 @@ template <
|
||||
|
||||
case Operation::XLAT: Primitive::xlat<AddressT>(instruction, memory, registers); return;
|
||||
|
||||
case Operation::POP: source() = Primitive::pop<IntT>(memory, registers); break;
|
||||
case Operation::PUSH: Primitive::push<IntT>(source(), memory, registers); break;
|
||||
case Operation::POP: destination_w() = Primitive::pop<IntT>(memory, registers); break;
|
||||
case Operation::PUSH: Primitive::push<IntT>(source_r(), memory, registers); break;
|
||||
case Operation::POPF: Primitive::popf(memory, registers, status); break;
|
||||
case Operation::PUSHF: Primitive::pushf(memory, registers, status); break;
|
||||
|
||||
|
@ -179,7 +179,16 @@ struct Memory {
|
||||
return write_back_value_;
|
||||
}
|
||||
}
|
||||
return access<IntT, type>(segment, address, Tag::Accessed);
|
||||
auto &value = access<IntT, type>(segment, address, Tag::Accessed);
|
||||
|
||||
// For testing purposes: if the CPU indicated it'll only be reading, copy the requested value into a temporary
|
||||
// location so that any writes will be discarded.
|
||||
if(type == AccessType::Read) {
|
||||
*reinterpret_cast<IntT *>(&read_value_) = value;
|
||||
return *reinterpret_cast<IntT *>(&read_value_);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename IntT>
|
||||
@ -196,6 +205,7 @@ struct Memory {
|
||||
static constexpr uint32_t NoWriteBack = 0; // A low byte address of 0 can't require write-back.
|
||||
uint32_t write_back_address_[2] = {NoWriteBack, NoWriteBack};
|
||||
uint16_t write_back_value_;
|
||||
uint16_t read_value_;
|
||||
};
|
||||
struct IO {
|
||||
template <typename IntT> void out([[maybe_unused]] uint16_t port, [[maybe_unused]] IntT value) {}
|
||||
@ -385,6 +395,8 @@ struct FailedExecution {
|
||||
return hexInstruction;
|
||||
};
|
||||
|
||||
EACCES;
|
||||
|
||||
const auto decoded = decoder.decode(data.data(), data.size());
|
||||
const bool sizeMatched = decoded.first == data.size();
|
||||
if(assert) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user