1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-25 18:30:07 +00:00

Start promotion of ReturnType.

This commit is contained in:
Thomas Harte 2023-11-05 21:42:22 -05:00
parent f96c33102a
commit 009915f4de
6 changed files with 273 additions and 221 deletions

View File

@ -0,0 +1,56 @@
//
// AccessType.hpp
// Clock Signal
//
// Created by Thomas Harte on 05/11/2023.
// Copyright © 2023 Thomas Harte. All rights reserved.
//
#ifndef AccessType_h
#define AccessType_h
namespace InstructionSet::x86 {
/// Explains the type of access that `perform` intends to perform; is provided as a template parameter to whatever
/// the caller supplies as `MemoryT` and `RegistersT` when obtaining a reference to whatever the processor
/// intends to reference.
///
/// `perform` guarantees to validate all accesses before modifying any state, giving the caller opportunity to generate
/// any exceptions that might be applicable.
enum class AccessType {
/// The requested value will be read from.
Read,
/// The requested value will be written to.
Write,
/// The requested value will be read from and then written to.
ReadModifyWrite,
/// The requested value has already been authorised for whatever form of access is now intended, so there's no
/// need further to inspect. This is done e.g. by operations that will push multiple values to the stack to verify that
/// all necessary stack space is available ahead of pushing anything, though each individual push will then result in
/// a further `Preauthorised` access.
PreauthorisedRead,
};
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 custom type that can be written but not read.
template <typename IntT>
class Writeable {
public:
Writeable(IntT &target) : target_(target) {}
void operator=(IntT value) { target_ = value; }
private:
IntT &target_;
};
template <typename IntT> struct ReturnType<IntT, AccessType::Write> { using type = Writeable<IntT>; };
// Read-modify-writes: return a reference.
template <typename IntT> struct ReturnType<IntT, AccessType::ReadModifyWrite> { using type = IntT &; };
}
#endif /* AccessType_h */

View File

@ -13,199 +13,13 @@
#include "../../../Numeric/Carry.hpp"
#include "../../../Numeric/RegisterSizes.hpp"
#include "../Interrupts.hpp"
#include "../AccessType.hpp"
#include "Resolver.hpp"
#include <utility>
namespace InstructionSet::x86 {
template <typename IntT, AccessType access, typename InstructionT, typename ContextT>
IntT *resolve(
InstructionT &instruction,
Source source,
DataPointer pointer,
ContextT &context,
IntT *none = nullptr,
IntT *immediate = nullptr
);
/// Calculates the absolute address for @c pointer given the registers and memory provided in @c context and taking any
/// referenced offset from @c instruction.
template <Source source, typename IntT, AccessType access, typename InstructionT, typename ContextT>
uint32_t address(
InstructionT &instruction,
DataPointer pointer,
ContextT &context
) {
if constexpr (source == Source::DirectAddress) {
return instruction.offset();
}
uint32_t address;
uint16_t zero = 0;
address = *resolve<uint16_t, access>(instruction, pointer.index(), pointer, context, &zero);
if constexpr (is_32bit(ContextT::model)) {
address <<= pointer.scale();
}
address += instruction.offset();
if constexpr (source == Source::IndirectNoBase) {
return address;
}
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;
/// @c nullptr otherwise. @c access is currently unused but is intended to provide the hook upon which updates to
/// segment registers can be tracked for protected modes.
template <typename IntT, AccessType access, Source source, typename ContextT>
IntT *register_(ContextT &context) {
static constexpr bool supports_dword = is_32bit(ContextT::model);
switch(source) {
case Source::eAX:
// Slightly contorted if chain here and below:
//
// (i) does the `constexpr` version of a `switch`; and
// (i) ensures .eax() etc aren't called on @c registers for 16-bit processors, so they need not implement 32-bit storage.
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.eax(); }
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &context.registers.ax(); }
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &context.registers.al(); }
else { return nullptr; }
case Source::eCX:
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.ecx(); }
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &context.registers.cx(); }
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &context.registers.cl(); }
else { return nullptr; }
case Source::eDX:
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.edx(); }
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &context.registers.dx(); }
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &context.registers.dl(); }
else if constexpr (std::is_same_v<IntT, uint32_t>) { return nullptr; }
case Source::eBX:
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.ebx(); }
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &context.registers.bx(); }
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &context.registers.bl(); }
else if constexpr (std::is_same_v<IntT, uint32_t>) { return nullptr; }
case Source::eSPorAH:
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.esp(); }
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &context.registers.sp(); }
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &context.registers.ah(); }
else { return nullptr; }
case Source::eBPorCH:
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.ebp(); }
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &context.registers.bp(); }
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &context.registers.ch(); }
else { return nullptr; }
case Source::eSIorDH:
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.esi(); }
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &context.registers.si(); }
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &context.registers.dh(); }
else { return nullptr; }
case Source::eDIorBH:
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.edi(); }
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &context.registers.di(); }
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &context.registers.bh(); }
else { return nullptr; }
// Segment registers are always 16-bit.
case Source::ES: if constexpr (std::is_same_v<IntT, uint16_t>) return &context.registers.es(); else return nullptr;
case Source::CS: if constexpr (std::is_same_v<IntT, uint16_t>) return &context.registers.cs(); else return nullptr;
case Source::SS: if constexpr (std::is_same_v<IntT, uint16_t>) return &context.registers.ss(); else return nullptr;
case Source::DS: if constexpr (std::is_same_v<IntT, uint16_t>) return &context.registers.ds(); else return nullptr;
// 16-bit models don't have FS and GS.
case Source::FS: if constexpr (is_32bit(ContextT::model) && std::is_same_v<IntT, uint16_t>) return &context.registers.fs(); else return nullptr;
case Source::GS: if constexpr (is_32bit(ContextT::model) && std::is_same_v<IntT, uint16_t>) return &context.registers.gs(); else return nullptr;
default: return nullptr;
}
}
///Obtains the address described by @c pointer from @c instruction given the registers and memory as described by @c context.
template <typename IntT, AccessType access, typename InstructionT, typename ContextT>
uint32_t address(
InstructionT &instruction,
DataPointer pointer,
ContextT &context
) {
// TODO: at least on the 8086 this isn't how register 'addresses' are resolved; instead whatever was the last computed address
// remains in the address register and is returned. Find out what other x86s do and make a decision.
switch(pointer.source()) {
default: return 0;
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::Indirect: return address<Source::Indirect, IntT, access>(instruction, pointer, context);
case Source::IndirectNoBase: return address<Source::IndirectNoBase, IntT, access>(instruction, pointer, context);
case Source::DirectAddress: return address<Source::DirectAddress, IntT, access>(instruction, pointer, context);
}
}
/// Obtain a pointer to the value desribed by @c source, which is one of those named by @c pointer, using @c instruction and @c context
/// for offsets, registers and memory contents.
///
/// If @c source is Source::None then @c none is returned.
///
/// 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(
InstructionT &instruction,
Source source,
DataPointer pointer,
ContextT &context,
IntT *none,
IntT *immediate
) {
// Rules:
//
// * if this is a memory access, set target_address and break;
// * otherwise return the appropriate value.
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::None: return none;
case Source::Immediate:
*immediate = instruction.operand();
return immediate;
case Source::Indirect:
target_address = address<Source::Indirect, IntT, access>(instruction, pointer, context);
break;
case Source::IndirectNoBase:
target_address = address<Source::IndirectNoBase, IntT, access>(instruction, pointer, context);
break;
case Source::DirectAddress:
target_address = address<Source::DirectAddress, IntT, access>(instruction, pointer, context);
break;
}
// 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);
};
namespace Primitive {
// The below takes a reference in order properly to handle PUSH SP,

View File

@ -0,0 +1,207 @@
//
// Resolver.hpp
// Clock Signal
//
// Created by Thomas Harte on 05/11/2023.
// Copyright © 2023 Thomas Harte. All rights reserved.
//
#ifndef Resolver_h
#define Resolver_h
#include "../AccessType.hpp"
namespace InstructionSet::x86 {
/// Obtain a pointer to the value desribed by @c source, which is one of those named by @c pointer, using @c instruction and @c context
/// for offsets, registers and memory contents.
///
/// If @c source is Source::None then @c none is returned.
///
/// 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(
InstructionT &instruction,
Source source,
DataPointer pointer,
ContextT &context,
IntT *none = nullptr,
IntT *immediate = nullptr
);
/// Calculates the absolute address for @c pointer given the registers and memory provided in @c context and taking any
/// referenced offset from @c instruction.
template <Source source, typename IntT, AccessType access, typename InstructionT, typename ContextT>
uint32_t address(
InstructionT &instruction,
DataPointer pointer,
ContextT &context
) {
if constexpr (source == Source::DirectAddress) {
return instruction.offset();
}
uint32_t address;
uint16_t zero = 0;
address = *resolve<uint16_t, access>(instruction, pointer.index(), pointer, context, &zero);
if constexpr (is_32bit(ContextT::model)) {
address <<= pointer.scale();
}
address += instruction.offset();
if constexpr (source == Source::IndirectNoBase) {
return address;
}
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;
/// @c nullptr otherwise. @c access is currently unused but is intended to provide the hook upon which updates to
/// segment registers can be tracked for protected modes.
template <typename IntT, AccessType access, Source source, typename ContextT>
IntT *register_(ContextT &context) {
static constexpr bool supports_dword = is_32bit(ContextT::model);
switch(source) {
case Source::eAX:
// Slightly contorted if chain here and below:
//
// (i) does the `constexpr` version of a `switch`; and
// (i) ensures .eax() etc aren't called on @c registers for 16-bit processors, so they need not implement 32-bit storage.
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.eax(); }
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &context.registers.ax(); }
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &context.registers.al(); }
else { return nullptr; }
case Source::eCX:
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.ecx(); }
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &context.registers.cx(); }
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &context.registers.cl(); }
else { return nullptr; }
case Source::eDX:
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.edx(); }
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &context.registers.dx(); }
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &context.registers.dl(); }
else if constexpr (std::is_same_v<IntT, uint32_t>) { return nullptr; }
case Source::eBX:
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.ebx(); }
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &context.registers.bx(); }
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &context.registers.bl(); }
else if constexpr (std::is_same_v<IntT, uint32_t>) { return nullptr; }
case Source::eSPorAH:
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.esp(); }
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &context.registers.sp(); }
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &context.registers.ah(); }
else { return nullptr; }
case Source::eBPorCH:
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.ebp(); }
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &context.registers.bp(); }
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &context.registers.ch(); }
else { return nullptr; }
case Source::eSIorDH:
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.esi(); }
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &context.registers.si(); }
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &context.registers.dh(); }
else { return nullptr; }
case Source::eDIorBH:
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.edi(); }
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &context.registers.di(); }
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &context.registers.bh(); }
else { return nullptr; }
// Segment registers are always 16-bit.
case Source::ES: if constexpr (std::is_same_v<IntT, uint16_t>) return &context.registers.es(); else return nullptr;
case Source::CS: if constexpr (std::is_same_v<IntT, uint16_t>) return &context.registers.cs(); else return nullptr;
case Source::SS: if constexpr (std::is_same_v<IntT, uint16_t>) return &context.registers.ss(); else return nullptr;
case Source::DS: if constexpr (std::is_same_v<IntT, uint16_t>) return &context.registers.ds(); else return nullptr;
// 16-bit models don't have FS and GS.
case Source::FS: if constexpr (is_32bit(ContextT::model) && std::is_same_v<IntT, uint16_t>) return &context.registers.fs(); else return nullptr;
case Source::GS: if constexpr (is_32bit(ContextT::model) && std::is_same_v<IntT, uint16_t>) return &context.registers.gs(); else return nullptr;
default: return nullptr;
}
}
///Obtains the address described by @c pointer from @c instruction given the registers and memory as described by @c context.
template <typename IntT, AccessType access, typename InstructionT, typename ContextT>
uint32_t address(
InstructionT &instruction,
DataPointer pointer,
ContextT &context
) {
// TODO: at least on the 8086 this isn't how register 'addresses' are resolved; instead whatever was the last computed address
// remains in the address register and is returned. Find out what other x86s do and make a decision.
switch(pointer.source()) {
default: return 0;
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::Indirect: return address<Source::Indirect, IntT, access>(instruction, pointer, context);
case Source::IndirectNoBase: return address<Source::IndirectNoBase, IntT, access>(instruction, pointer, context);
case Source::DirectAddress: return address<Source::DirectAddress, IntT, access>(instruction, pointer, context);
}
}
// See forward declaration, above, for details.
template <typename IntT, AccessType access, typename InstructionT, typename ContextT>
IntT *resolve(
InstructionT &instruction,
Source source,
DataPointer pointer,
ContextT &context,
IntT *none,
IntT *immediate
) {
// Rules:
//
// * if this is a memory access, set target_address and break;
// * otherwise return the appropriate value.
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::None: return none;
case Source::Immediate:
*immediate = instruction.operand();
return immediate;
case Source::Indirect:
target_address = address<Source::Indirect, IntT, access>(instruction, pointer, context);
break;
case Source::IndirectNoBase:
target_address = address<Source::IndirectNoBase, IntT, access>(instruction, pointer, context);
break;
case Source::DirectAddress:
target_address = address<Source::DirectAddress, IntT, access>(instruction, pointer, context);
break;
}
// 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);
}
}
#endif /* Resolver_h */

View File

@ -15,26 +15,6 @@
namespace InstructionSet::x86 {
/// Explains the type of access that `perform` intends to perform; is provided as a template parameter to whatever
/// the caller supplies as `MemoryT` and `RegistersT` when obtaining a reference to whatever the processor
/// intends to reference.
///
/// `perform` guarantees to validate all accesses before modifying any state, giving the caller opportunity to generate
/// any exceptions that might be applicable.
enum class AccessType {
/// The requested value will be read from.
Read,
/// The requested value will be written to.
Write,
/// The requested value will be read from and then written to.
ReadModifyWrite,
/// The requested value has already been authorised for whatever form of access is now intended, so there's no
/// need further to inspect. This is done e.g. by operations that will push multiple values to the stack to verify that
/// all necessary stack space is available ahead of pushing anything, though each individual push will then result in
/// a further `Preauthorised` access.
PreauthorisedRead,
};
template <
Model model_,
typename FlowControllerT,

View File

@ -1150,6 +1150,8 @@
42A5E8412ABBE16F00A0DD5D /* nop_test.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = nop_test.bin; sourceTree = "<group>"; };
42A5E8422ABBE16F00A0DD5D /* lax_test.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = lax_test.bin; sourceTree = "<group>"; };
42A5E8432ABBE16F00A0DD5D /* branch_backwards_test.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = branch_backwards_test.bin; sourceTree = "<group>"; };
42AA41232AF888370016751C /* AccessType.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = AccessType.hpp; sourceTree = "<group>"; };
42AA41242AF8893F0016751C /* Resolver.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Resolver.hpp; sourceTree = "<group>"; };
42AD552E2A0C4D5000ACE410 /* 68000.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 68000.hpp; sourceTree = "<group>"; };
42AD55302A0C4D5000ACE410 /* 68000Storage.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 68000Storage.hpp; sourceTree = "<group>"; };
42AD55312A0C4D5000ACE410 /* 68000Implementation.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 68000Implementation.hpp; sourceTree = "<group>"; };
@ -2328,6 +2330,7 @@
isa = PBXGroup;
children = (
42437B382ACF2798006DFED1 /* PerformImplementation.hpp */,
42AA41242AF8893F0016751C /* Resolver.hpp */,
);
path = Implementation;
sourceTree = "<group>";
@ -4997,12 +5000,13 @@
children = (
4BEDA3B925B25563000C2DBD /* Decoder.cpp */,
4B69DEB52AB79E4F0055B217 /* Instruction.cpp */,
42AA41232AF888370016751C /* AccessType.hpp */,
4BEDA3B825B25563000C2DBD /* Decoder.hpp */,
42437B342ACF02A9006DFED1 /* Flags.hpp */,
4BEDA3DB25B2588F000C2DBD /* Instruction.hpp */,
42437B392AD07465006DFED1 /* Interrupts.hpp */,
4BE3C69527CBC540000EAD28 /* Model.hpp */,
42437B352ACF0AA2006DFED1 /* Perform.hpp */,
42437B342ACF02A9006DFED1 /* Flags.hpp */,
42437B372ACF2798006DFED1 /* Implementation */,
);
path = x86;

View File

@ -19,6 +19,7 @@
#include "NSData+dataWithContentsOfGZippedFile.h"
#include "../../../InstructionSets/x86/AccessType.hpp"
#include "../../../InstructionSets/x86/Decoder.hpp"
#include "../../../InstructionSets/x86/Perform.hpp"
#include "../../../InstructionSets/x86/Flags.hpp"
@ -97,16 +98,6 @@ 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 &; };
template <typename IntT> struct ReturnType<IntT, AccessType::ReadModifyWrite> { using type = IntT &; };
// Constructor.
Memory(Registers &registers) : registers_(registers) {
memory.resize(1024*1024);
@ -162,7 +153,7 @@ struct Memory {
// Accesses an address based on segment:offset.
template <typename IntT, AccessType type>
typename ReturnType<IntT, type>::type &access(InstructionSet::x86::Source segment, uint16_t offset) {
typename InstructionSet::x86::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.
@ -185,7 +176,7 @@ struct Memory {
// Accesses an address based on physical location.
template <typename IntT, AccessType type>
typename ReturnType<IntT, type>::type &access(uint32_t address) {
typename InstructionSet::x86::ReturnType<IntT, type>::type &access(uint32_t address) {
return access<IntT, type>(address, Tag::Accessed);
}