mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-11 08:30:55 +00:00
Start promotion of ReturnType.
This commit is contained in:
parent
f96c33102a
commit
009915f4de
56
InstructionSets/x86/AccessType.hpp
Normal file
56
InstructionSets/x86/AccessType.hpp
Normal 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 */
|
@ -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,
|
||||
|
207
InstructionSets/x86/Implementation/Resolver.hpp
Normal file
207
InstructionSets/x86/Implementation/Resolver.hpp
Normal 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 */
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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 ®isters) : 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);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user