1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-29 00:29:34 +00:00

Ensure that reads can only read, accept that source is sometimes written to. E.g. XCHG.

This commit is contained in:
Thomas Harte 2023-10-31 15:06:19 -04:00
parent 02af08ffd2
commit 1d479ec2d7
3 changed files with 208 additions and 170 deletions

View File

@ -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(

View File

@ -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;

View File

@ -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) {