1
0
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:
Thomas Harte 2023-11-06 16:04:31 -05:00
parent 797c9fe129
commit 2af774601f
4 changed files with 59 additions and 41 deletions

View File

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

View File

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

View File

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

View File

@ -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 &registers) : 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");