mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-25 16:31:42 +00:00
Temporarily disentangle Memory
and access internals; start to be overt in PerformImplementation.
This commit is contained in:
parent
797c9fe129
commit
2af774601f
@ -31,11 +31,11 @@ enum class AccessType {
|
||||
PreauthorisedRead,
|
||||
};
|
||||
|
||||
template <typename IntT, AccessType type> struct ReturnType;
|
||||
template <typename IntT, AccessType type> struct Accessor;
|
||||
|
||||
// Reads: return a value directly.
|
||||
template <typename IntT> struct ReturnType<IntT, AccessType::Read> { using type = IntT; };
|
||||
template <typename IntT> struct ReturnType<IntT, AccessType::PreauthorisedRead> { using type = IntT; };
|
||||
template <typename IntT> struct Accessor<IntT, AccessType::Read> { using type = IntT; };
|
||||
template <typename IntT> struct Accessor<IntT, AccessType::PreauthorisedRead> { using type = IntT; };
|
||||
|
||||
// Writes: return a custom type that can be written but not read.
|
||||
template <typename IntT>
|
||||
@ -46,11 +46,16 @@ class Writeable {
|
||||
private:
|
||||
IntT &target_;
|
||||
};
|
||||
//template <typename IntT> struct ReturnType<IntT, AccessType::Write> { using type = Writeable<IntT>; };
|
||||
template <typename IntT> struct ReturnType<IntT, AccessType::Write> { using type = IntT &; };
|
||||
//template <typename IntT> struct Accessor<IntT, AccessType::Write> { using type = Writeable<IntT>; };
|
||||
template <typename IntT> struct Accessor<IntT, AccessType::Write> { using type = IntT &; };
|
||||
|
||||
// Read-modify-writes: return a reference.
|
||||
template <typename IntT> struct ReturnType<IntT, AccessType::ReadModifyWrite> { using type = IntT &; };
|
||||
template <typename IntT> struct Accessor<IntT, AccessType::ReadModifyWrite> { using type = IntT &; };
|
||||
|
||||
// Shorthands; assumed that preauthorised reads have the same return type as reads.
|
||||
template<typename IntT> using read_t = typename Accessor<IntT, AccessType::Read>::type;
|
||||
template<typename IntT> using write_t = typename Accessor<IntT, AccessType::Write>::type;
|
||||
template<typename IntT> using modify_t = typename Accessor<IntT, AccessType::ReadModifyWrite>::type;
|
||||
|
||||
}
|
||||
|
||||
|
@ -279,8 +279,12 @@ void add(IntT &destination, IntT source, ContextT &context) {
|
||||
destination = result;
|
||||
}
|
||||
|
||||
template <bool with_borrow, bool write_back, typename IntT, typename ContextT>
|
||||
void sub(IntT &destination, IntT source, ContextT &context) {
|
||||
template <bool with_borrow, AccessType destination_type, typename IntT, typename ContextT>
|
||||
void sub(
|
||||
typename Accessor<IntT, destination_type>::type destination,
|
||||
read_t<IntT> source,
|
||||
ContextT &context
|
||||
) {
|
||||
/*
|
||||
DEST ← DEST - (SRC [+ CF]);
|
||||
*/
|
||||
@ -298,7 +302,7 @@ void sub(IntT &destination, IntT source, ContextT &context) {
|
||||
|
||||
context.flags.template set_from<IntT, Flag::Zero, Flag::Sign, Flag::ParityOdd>(result);
|
||||
|
||||
if constexpr (write_back) {
|
||||
if constexpr (destination_type == AccessType::Write) {
|
||||
destination = result;
|
||||
}
|
||||
}
|
||||
@ -1374,8 +1378,8 @@ template <
|
||||
//
|
||||
// (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_r = [&]() -> IntT& {
|
||||
return *resolve<IntT, AccessType::Read>(
|
||||
const auto source_r = [&]() -> IntT {
|
||||
return resolve<IntT, AccessType::Read>(
|
||||
instruction,
|
||||
instruction.source().source(),
|
||||
instruction.source(),
|
||||
@ -1384,7 +1388,7 @@ template <
|
||||
&immediate);
|
||||
};
|
||||
const auto source_rmw = [&]() -> IntT& {
|
||||
return *resolve<IntT, AccessType::ReadModifyWrite>(
|
||||
return resolve<IntT, AccessType::ReadModifyWrite>(
|
||||
instruction,
|
||||
instruction.source().source(),
|
||||
instruction.source(),
|
||||
@ -1392,8 +1396,8 @@ template <
|
||||
nullptr,
|
||||
&immediate);
|
||||
};
|
||||
const auto destination_r = [&]() -> IntT& {
|
||||
return *resolve<IntT, AccessType::Read>(
|
||||
const auto destination_r = [&]() -> IntT {
|
||||
return resolve<IntT, AccessType::Read>(
|
||||
instruction,
|
||||
instruction.destination().source(),
|
||||
instruction.destination(),
|
||||
@ -1402,7 +1406,7 @@ template <
|
||||
&immediate);
|
||||
};
|
||||
const auto destination_w = [&]() -> IntT& {
|
||||
return *resolve<IntT, AccessType::Write>(
|
||||
return resolve<IntT, AccessType::Write>(
|
||||
instruction,
|
||||
instruction.destination().source(),
|
||||
instruction.destination(),
|
||||
@ -1411,7 +1415,7 @@ template <
|
||||
&immediate);
|
||||
};
|
||||
const auto destination_rmw = [&]() -> IntT& {
|
||||
return *resolve<IntT, AccessType::ReadModifyWrite>(
|
||||
return resolve<IntT, AccessType::ReadModifyWrite>(
|
||||
instruction,
|
||||
instruction.destination().source(),
|
||||
instruction.destination(),
|
||||
|
@ -21,7 +21,7 @@ namespace InstructionSet::x86 {
|
||||
/// If @c source is Source::Immediate then the appropriate portion of @c instrucion's operand
|
||||
/// is copied to @c *immediate and @c immediate is returned.
|
||||
template <typename IntT, AccessType access, typename InstructionT, typename ContextT>
|
||||
IntT *resolve(
|
||||
typename Accessor<IntT, access>::type resolve(
|
||||
InstructionT &instruction,
|
||||
Source source,
|
||||
DataPointer pointer,
|
||||
@ -44,7 +44,7 @@ uint32_t address(
|
||||
|
||||
uint32_t address;
|
||||
uint16_t zero = 0;
|
||||
address = *resolve<uint16_t, access>(instruction, pointer.index(), pointer, context, &zero);
|
||||
address = resolve<uint16_t, access>(instruction, pointer.index(), pointer, context, &zero);
|
||||
if constexpr (is_32bit(ContextT::model)) {
|
||||
address <<= pointer.scale();
|
||||
}
|
||||
@ -53,7 +53,7 @@ uint32_t address(
|
||||
if constexpr (source == Source::IndirectNoBase) {
|
||||
return address;
|
||||
}
|
||||
return address + *resolve<uint16_t, access>(instruction, pointer.base(), pointer, context);
|
||||
return address + resolve<uint16_t, access>(instruction, pointer.base(), pointer, context);
|
||||
}
|
||||
|
||||
/// @returns a pointer to the contents of the register identified by the combination of @c IntT and @c Source if any;
|
||||
@ -150,7 +150,7 @@ uint32_t address(
|
||||
|
||||
// See forward declaration, above, for details.
|
||||
template <typename IntT, AccessType access, typename InstructionT, typename ContextT>
|
||||
IntT *resolve(
|
||||
typename Accessor<IntT, access>::type resolve(
|
||||
InstructionT &instruction,
|
||||
Source source,
|
||||
DataPointer pointer,
|
||||
@ -165,26 +165,26 @@ IntT *resolve(
|
||||
uint32_t target_address;
|
||||
switch(source) {
|
||||
// Defer all register accesses to the register-specific lookup.
|
||||
case Source::eAX: return register_<IntT, access, Source::eAX>(context);
|
||||
case Source::eCX: return register_<IntT, access, Source::eCX>(context);
|
||||
case Source::eDX: return register_<IntT, access, Source::eDX>(context);
|
||||
case Source::eBX: return register_<IntT, access, Source::eBX>(context);
|
||||
case Source::eSPorAH: return register_<IntT, access, Source::eSPorAH>(context);
|
||||
case Source::eBPorCH: return register_<IntT, access, Source::eBPorCH>(context);
|
||||
case Source::eSIorDH: return register_<IntT, access, Source::eSIorDH>(context);
|
||||
case Source::eDIorBH: return register_<IntT, access, Source::eDIorBH>(context);
|
||||
case Source::ES: return register_<IntT, access, Source::ES>(context);
|
||||
case Source::CS: return register_<IntT, access, Source::CS>(context);
|
||||
case Source::SS: return register_<IntT, access, Source::SS>(context);
|
||||
case Source::DS: return register_<IntT, access, Source::DS>(context);
|
||||
case Source::FS: return register_<IntT, access, Source::FS>(context);
|
||||
case Source::GS: return register_<IntT, access, Source::GS>(context);
|
||||
case Source::eAX: return *register_<IntT, access, Source::eAX>(context);
|
||||
case Source::eCX: return *register_<IntT, access, Source::eCX>(context);
|
||||
case Source::eDX: return *register_<IntT, access, Source::eDX>(context);
|
||||
case Source::eBX: return *register_<IntT, access, Source::eBX>(context);
|
||||
case Source::eSPorAH: return *register_<IntT, access, Source::eSPorAH>(context);
|
||||
case Source::eBPorCH: return *register_<IntT, access, Source::eBPorCH>(context);
|
||||
case Source::eSIorDH: return *register_<IntT, access, Source::eSIorDH>(context);
|
||||
case Source::eDIorBH: return *register_<IntT, access, Source::eDIorBH>(context);
|
||||
case Source::ES: return *register_<IntT, access, Source::ES>(context);
|
||||
case Source::CS: return *register_<IntT, access, Source::CS>(context);
|
||||
case Source::SS: return *register_<IntT, access, Source::SS>(context);
|
||||
case Source::DS: return *register_<IntT, access, Source::DS>(context);
|
||||
case Source::FS: return *register_<IntT, access, Source::FS>(context);
|
||||
case Source::GS: return *register_<IntT, access, Source::GS>(context);
|
||||
|
||||
case Source::None: return none;
|
||||
case Source::None: return *none;
|
||||
|
||||
case Source::Immediate:
|
||||
*immediate = instruction.operand();
|
||||
return immediate;
|
||||
return *immediate;
|
||||
|
||||
case Source::Indirect:
|
||||
target_address = address<Source::Indirect, IntT, access>(instruction, pointer, context);
|
||||
@ -199,7 +199,7 @@ IntT *resolve(
|
||||
|
||||
// If execution has reached here then a memory fetch is required.
|
||||
// Do it and exit.
|
||||
return &context.memory.template access<IntT, access>(instruction.data_segment(), target_address);
|
||||
return context.memory.template access<IntT, access>(instruction.data_segment(), target_address);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -98,6 +98,15 @@ struct Memory {
|
||||
public:
|
||||
using AccessType = InstructionSet::x86::AccessType;
|
||||
|
||||
template <typename IntT, AccessType type> struct ReturnType;
|
||||
|
||||
// Reads: return a value directly.
|
||||
template <typename IntT> struct ReturnType<IntT, AccessType::Read> { using type = IntT; };
|
||||
template <typename IntT> struct ReturnType<IntT, AccessType::PreauthorisedRead> { using type = IntT; };
|
||||
|
||||
// Writes: return a reference.
|
||||
template <typename IntT> struct ReturnType<IntT, AccessType::Write> { using type = IntT &; };
|
||||
|
||||
// Constructor.
|
||||
Memory(Registers ®isters) : registers_(registers) {
|
||||
memory.resize(1024*1024);
|
||||
@ -153,7 +162,7 @@ struct Memory {
|
||||
|
||||
// Accesses an address based on segment:offset.
|
||||
template <typename IntT, AccessType type>
|
||||
typename InstructionSet::x86::ReturnType<IntT, type>::type &access(InstructionSet::x86::Source segment, uint16_t offset) {
|
||||
typename ReturnType<IntT, type>::type &access(InstructionSet::x86::Source segment, uint16_t offset) {
|
||||
if constexpr (std::is_same_v<IntT, uint16_t>) {
|
||||
// If this is a 16-bit access that runs past the end of the segment, it'll wrap back
|
||||
// to the start. So the 16-bit value will need to be a local cache.
|
||||
@ -176,7 +185,7 @@ struct Memory {
|
||||
|
||||
// Accesses an address based on physical location.
|
||||
template <typename IntT, AccessType type>
|
||||
typename InstructionSet::x86::ReturnType<IntT, type>::type &access(uint32_t address) {
|
||||
typename ReturnType<IntT, type>::type &access(uint32_t address) {
|
||||
return access<IntT, type>(address, Tag::Accessed);
|
||||
}
|
||||
|
||||
@ -273,7 +282,7 @@ struct Memory {
|
||||
// Entry point used by the flow controller so that it can mark up locations at which the flags were written,
|
||||
// so that defined-flag-only masks can be applied while verifying RAM contents.
|
||||
template <typename IntT, AccessType type>
|
||||
typename InstructionSet::x86::ReturnType<IntT, type>::type &access(InstructionSet::x86::Source segment, uint16_t offset, Tag tag) {
|
||||
typename ReturnType<IntT, type>::type &access(InstructionSet::x86::Source segment, uint16_t offset, Tag tag) {
|
||||
const uint32_t physical_address = address(segment, offset);
|
||||
return access<IntT, type>(physical_address, tag);
|
||||
}
|
||||
@ -281,7 +290,7 @@ struct Memory {
|
||||
// An additional entry point for the flow controller; on the original 8086 interrupt vectors aren't relative
|
||||
// to a selector, they're just at an absolute location.
|
||||
template <typename IntT, AccessType type>
|
||||
typename InstructionSet::x86::ReturnType<IntT, type>::type &access(uint32_t address, Tag tag) {
|
||||
typename ReturnType<IntT, type>::type &access(uint32_t address, Tag tag) {
|
||||
if constexpr (type == AccessType::PreauthorisedRead) {
|
||||
if(!test_preauthorisation(address)) {
|
||||
printf("Non preauthorised access\n");
|
||||
|
Loading…
Reference in New Issue
Block a user