mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-25 18:30:21 +00:00
Merge pull request #1198 from TomHarte/DirectWrite
Add compiler assistance on access types
This commit is contained in:
commit
0fee3ff92c
66
InstructionSets/x86/AccessType.hpp
Normal file
66
InstructionSets/x86/AccessType.hpp
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
//
|
||||||
|
// 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,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr bool is_writeable(AccessType type) {
|
||||||
|
return type == AccessType::ReadModifyWrite || type == AccessType::Write;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, AccessType type> struct Accessor;
|
||||||
|
|
||||||
|
// Reads: return a value directly.
|
||||||
|
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>
|
||||||
|
class Writeable {
|
||||||
|
public:
|
||||||
|
Writeable(IntT &target) : target_(target) {}
|
||||||
|
IntT operator=(IntT value) { return target_ = value; }
|
||||||
|
private:
|
||||||
|
IntT &target_;
|
||||||
|
};
|
||||||
|
template <typename IntT> struct Accessor<IntT, AccessType::Write> { using type = Writeable<IntT>; };
|
||||||
|
|
||||||
|
// Read-modify-writes: return a reference.
|
||||||
|
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;
|
||||||
|
template<typename IntT, AccessType type> using access_t = typename Accessor<IntT, type>::type;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* AccessType_h */
|
@ -163,19 +163,19 @@ std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(con
|
|||||||
|
|
||||||
PartialBlock(0x20, AND); break;
|
PartialBlock(0x20, AND); break;
|
||||||
case 0x26: segment_override_ = Source::ES; break;
|
case 0x26: segment_override_ = Source::ES; break;
|
||||||
case 0x27: Complete(DAA, eAX, eAX, DataSize::Byte); break;
|
case 0x27: Complete(DAA, None, None, DataSize::Byte); break;
|
||||||
|
|
||||||
PartialBlock(0x28, SUB); break;
|
PartialBlock(0x28, SUB); break;
|
||||||
case 0x2e: segment_override_ = Source::CS; break;
|
case 0x2e: segment_override_ = Source::CS; break;
|
||||||
case 0x2f: Complete(DAS, eAX, eAX, DataSize::Byte); break;
|
case 0x2f: Complete(DAS, None, None, DataSize::Byte); break;
|
||||||
|
|
||||||
PartialBlock(0x30, XOR); break;
|
PartialBlock(0x30, XOR); break;
|
||||||
case 0x36: segment_override_ = Source::SS; break;
|
case 0x36: segment_override_ = Source::SS; break;
|
||||||
case 0x37: Complete(AAA, eAX, eAX, DataSize::Word); break;
|
case 0x37: Complete(AAA, None, None, DataSize::Word); break;
|
||||||
|
|
||||||
PartialBlock(0x38, CMP); break;
|
PartialBlock(0x38, CMP); break;
|
||||||
case 0x3e: segment_override_ = Source::DS; break;
|
case 0x3e: segment_override_ = Source::DS; break;
|
||||||
case 0x3f: Complete(AAS, eAX, eAX, DataSize::Word); break;
|
case 0x3f: Complete(AAS, None, None, DataSize::Word); break;
|
||||||
|
|
||||||
#undef PartialBlock
|
#undef PartialBlock
|
||||||
|
|
||||||
@ -361,8 +361,8 @@ std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(con
|
|||||||
case 0x96: Complete(XCHG, eAX, eSI, data_size_); break;
|
case 0x96: Complete(XCHG, eAX, eSI, data_size_); break;
|
||||||
case 0x97: Complete(XCHG, eAX, eDI, data_size_); break;
|
case 0x97: Complete(XCHG, eAX, eDI, data_size_); break;
|
||||||
|
|
||||||
case 0x98: Complete(CBW, eAX, AH, data_size_); break;
|
case 0x98: Complete(CBW, None, None, data_size_); break;
|
||||||
case 0x99: Complete(CWD, eAX, eDX, data_size_); break;
|
case 0x99: Complete(CWD, None, None, data_size_); break;
|
||||||
case 0x9a: Far(CALLfar); break;
|
case 0x9a: Far(CALLfar); break;
|
||||||
case 0x9b: Complete(WAIT, None, None, DataSize::Byte); break;
|
case 0x9b: Complete(WAIT, None, None, DataSize::Byte); break;
|
||||||
case 0x9c: Complete(PUSHF, None, None, data_size_); break;
|
case 0x9c: Complete(PUSHF, None, None, data_size_); break;
|
||||||
|
@ -13,180 +13,13 @@
|
|||||||
#include "../../../Numeric/Carry.hpp"
|
#include "../../../Numeric/Carry.hpp"
|
||||||
#include "../../../Numeric/RegisterSizes.hpp"
|
#include "../../../Numeric/RegisterSizes.hpp"
|
||||||
#include "../Interrupts.hpp"
|
#include "../Interrupts.hpp"
|
||||||
|
#include "../AccessType.hpp"
|
||||||
|
#include "Resolver.hpp"
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace InstructionSet::x86 {
|
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
|
|
||||||
);
|
|
||||||
|
|
||||||
template <Source source, typename IntT, AccessType access, typename InstructionT, typename ContextT>
|
|
||||||
uint32_t address(
|
|
||||||
InstructionT &instruction,
|
|
||||||
DataPointer pointer,
|
|
||||||
ContextT &context
|
|
||||||
) {
|
|
||||||
// TODO: non-word indexes and bases.
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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; }
|
|
||||||
|
|
||||||
default: return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
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);
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
case Source::Immediate:
|
|
||||||
*immediate = instruction.operand();
|
|
||||||
return immediate;
|
|
||||||
|
|
||||||
case Source::None: return none;
|
|
||||||
|
|
||||||
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 {
|
namespace Primitive {
|
||||||
|
|
||||||
// The below takes a reference in order properly to handle PUSH SP,
|
// The below takes a reference in order properly to handle PUSH SP,
|
||||||
@ -194,9 +27,13 @@ namespace Primitive {
|
|||||||
template <typename IntT, bool preauthorised, typename ContextT>
|
template <typename IntT, bool preauthorised, typename ContextT>
|
||||||
void push(IntT &value, ContextT &context) {
|
void push(IntT &value, ContextT &context) {
|
||||||
context.registers.sp_ -= sizeof(IntT);
|
context.registers.sp_ -= sizeof(IntT);
|
||||||
context.memory.template access<IntT, preauthorised ? AccessType::PreauthorisedWrite : AccessType::Write>(
|
if constexpr (preauthorised) {
|
||||||
Source::SS,
|
context.memory.template preauthorised_write<IntT>(Source::SS, context.registers.sp_, value);
|
||||||
context.registers.sp_) = value;
|
} else {
|
||||||
|
context.memory.template access<IntT, AccessType::Write>(
|
||||||
|
Source::SS,
|
||||||
|
context.registers.sp_) = value;
|
||||||
|
}
|
||||||
context.memory.template write_back<IntT>();
|
context.memory.template write_back<IntT>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -421,7 +258,11 @@ void das(uint8_t &al, ContextT &context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <bool with_carry, typename IntT, typename ContextT>
|
template <bool with_carry, typename IntT, typename ContextT>
|
||||||
void add(IntT &destination, IntT source, ContextT &context) {
|
void add(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
read_t<IntT> source,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
DEST ← DEST + SRC [+ CF];
|
DEST ← DEST + SRC [+ CF];
|
||||||
*/
|
*/
|
||||||
@ -442,8 +283,12 @@ void add(IntT &destination, IntT source, ContextT &context) {
|
|||||||
destination = result;
|
destination = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool with_borrow, bool write_back, typename IntT, typename ContextT>
|
template <bool with_borrow, AccessType destination_type, typename IntT, typename ContextT>
|
||||||
void sub(IntT &destination, IntT source, ContextT &context) {
|
void sub(
|
||||||
|
access_t<IntT, destination_type> destination,
|
||||||
|
read_t<IntT> source,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
DEST ← DEST - (SRC [+ CF]);
|
DEST ← DEST - (SRC [+ CF]);
|
||||||
*/
|
*/
|
||||||
@ -461,13 +306,17 @@ void sub(IntT &destination, IntT source, ContextT &context) {
|
|||||||
|
|
||||||
context.flags.template set_from<IntT, Flag::Zero, Flag::Sign, Flag::ParityOdd>(result);
|
context.flags.template set_from<IntT, Flag::Zero, Flag::Sign, Flag::ParityOdd>(result);
|
||||||
|
|
||||||
if constexpr (write_back) {
|
if constexpr (is_writeable(destination_type)) {
|
||||||
destination = result;
|
destination = result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename ContextT>
|
template <typename IntT, typename ContextT>
|
||||||
void test(IntT &destination, IntT source, ContextT &context) {
|
void test(
|
||||||
|
read_t<IntT> destination,
|
||||||
|
read_t<IntT> source,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
TEMP ← SRC1 AND SRC2;
|
TEMP ← SRC1 AND SRC2;
|
||||||
SF ← MSB(TEMP);
|
SF ← MSB(TEMP);
|
||||||
@ -491,7 +340,10 @@ void test(IntT &destination, IntT source, ContextT &context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT>
|
template <typename IntT>
|
||||||
void xchg(IntT &destination, IntT &source) {
|
void xchg(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
modify_t<IntT> source
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
TEMP ← DEST
|
TEMP ← DEST
|
||||||
DEST ← SRC
|
DEST ← SRC
|
||||||
@ -501,7 +353,12 @@ void xchg(IntT &destination, IntT &source) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename ContextT>
|
template <typename IntT, typename ContextT>
|
||||||
void mul(IntT &destination_high, IntT &destination_low, IntT source, ContextT &context) {
|
void mul(
|
||||||
|
modify_t<IntT> destination_high,
|
||||||
|
modify_t<IntT> destination_low,
|
||||||
|
read_t<IntT> source,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
IF byte operation
|
IF byte operation
|
||||||
THEN
|
THEN
|
||||||
@ -523,7 +380,12 @@ void mul(IntT &destination_high, IntT &destination_low, IntT source, ContextT &c
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename ContextT>
|
template <typename IntT, typename ContextT>
|
||||||
void imul(IntT &destination_high, IntT &destination_low, IntT source, ContextT &context) {
|
void imul(
|
||||||
|
modify_t<IntT> destination_high,
|
||||||
|
modify_t<IntT> destination_low,
|
||||||
|
read_t<IntT> source,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
(as modified by https://www.felixcloutier.com/x86/daa ...)
|
(as modified by https://www.felixcloutier.com/x86/daa ...)
|
||||||
|
|
||||||
@ -558,7 +420,12 @@ void imul(IntT &destination_high, IntT &destination_low, IntT source, ContextT &
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename ContextT>
|
template <typename IntT, typename ContextT>
|
||||||
void div(IntT &destination_high, IntT &destination_low, IntT source, ContextT &context) {
|
void div(
|
||||||
|
modify_t<IntT> destination_high,
|
||||||
|
modify_t<IntT> destination_low,
|
||||||
|
read_t<IntT> source,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
IF SRC = 0
|
IF SRC = 0
|
||||||
THEN #DE; (* divide error *)
|
THEN #DE; (* divide error *)
|
||||||
@ -614,7 +481,12 @@ void div(IntT &destination_high, IntT &destination_low, IntT source, ContextT &c
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename ContextT>
|
template <typename IntT, typename ContextT>
|
||||||
void idiv(IntT &destination_high, IntT &destination_low, IntT source, ContextT &context) {
|
void idiv(
|
||||||
|
modify_t<IntT> destination_high,
|
||||||
|
modify_t<IntT> destination_low,
|
||||||
|
read_t<IntT> source,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
IF SRC = 0
|
IF SRC = 0
|
||||||
THEN #DE; (* divide error *)
|
THEN #DE; (* divide error *)
|
||||||
@ -671,7 +543,10 @@ void idiv(IntT &destination_high, IntT &destination_low, IntT source, ContextT &
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename ContextT>
|
template <typename IntT, typename ContextT>
|
||||||
void inc(IntT &destination, ContextT &context) {
|
void inc(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
DEST ← DEST + 1;
|
DEST ← DEST + 1;
|
||||||
*/
|
*/
|
||||||
@ -687,7 +562,11 @@ void inc(IntT &destination, ContextT &context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename ContextT>
|
template <typename IntT, typename ContextT>
|
||||||
void jump(bool condition, IntT displacement, ContextT &context) {
|
void jump(
|
||||||
|
bool condition,
|
||||||
|
IntT displacement,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
IF condition
|
IF condition
|
||||||
THEN
|
THEN
|
||||||
@ -706,7 +585,11 @@ void jump(bool condition, IntT displacement, ContextT &context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename OffsetT, typename ContextT>
|
template <typename IntT, typename OffsetT, typename ContextT>
|
||||||
void loop(IntT &counter, OffsetT displacement, ContextT &context) {
|
void loop(
|
||||||
|
modify_t<IntT> counter,
|
||||||
|
OffsetT displacement,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
--counter;
|
--counter;
|
||||||
if(counter) {
|
if(counter) {
|
||||||
context.flow_controller.jump(context.registers.ip() + displacement);
|
context.flow_controller.jump(context.registers.ip() + displacement);
|
||||||
@ -714,7 +597,11 @@ void loop(IntT &counter, OffsetT displacement, ContextT &context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename OffsetT, typename ContextT>
|
template <typename IntT, typename OffsetT, typename ContextT>
|
||||||
void loope(IntT &counter, OffsetT displacement, ContextT &context) {
|
void loope(
|
||||||
|
modify_t<IntT> counter,
|
||||||
|
OffsetT displacement,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
--counter;
|
--counter;
|
||||||
if(counter && context.flags.template flag<Flag::Zero>()) {
|
if(counter && context.flags.template flag<Flag::Zero>()) {
|
||||||
context.flow_controller.jump(context.registers.ip() + displacement);
|
context.flow_controller.jump(context.registers.ip() + displacement);
|
||||||
@ -722,7 +609,11 @@ void loope(IntT &counter, OffsetT displacement, ContextT &context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename OffsetT, typename ContextT>
|
template <typename IntT, typename OffsetT, typename ContextT>
|
||||||
void loopne(IntT &counter, OffsetT displacement, ContextT &context) {
|
void loopne(
|
||||||
|
modify_t<IntT> counter,
|
||||||
|
OffsetT displacement,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
--counter;
|
--counter;
|
||||||
if(counter && !context.flags.template flag<Flag::Zero>()) {
|
if(counter && !context.flags.template flag<Flag::Zero>()) {
|
||||||
context.flow_controller.jump(context.registers.ip() + displacement);
|
context.flow_controller.jump(context.registers.ip() + displacement);
|
||||||
@ -730,7 +621,10 @@ void loopne(IntT &counter, OffsetT displacement, ContextT &context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename ContextT>
|
template <typename IntT, typename ContextT>
|
||||||
void dec(IntT &destination, ContextT &context) {
|
void dec(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
DEST ← DEST - 1;
|
DEST ← DEST - 1;
|
||||||
*/
|
*/
|
||||||
@ -747,7 +641,11 @@ void dec(IntT &destination, ContextT &context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename ContextT>
|
template <typename IntT, typename ContextT>
|
||||||
void and_(IntT &destination, IntT source, ContextT &context) {
|
void and_(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
read_t<IntT> source,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
DEST ← DEST AND SRC;
|
DEST ← DEST AND SRC;
|
||||||
*/
|
*/
|
||||||
@ -762,7 +660,11 @@ void and_(IntT &destination, IntT source, ContextT &context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename ContextT>
|
template <typename IntT, typename ContextT>
|
||||||
void or_(IntT &destination, IntT source, ContextT &context) {
|
void or_(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
read_t<IntT> source,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
DEST ← DEST OR SRC;
|
DEST ← DEST OR SRC;
|
||||||
*/
|
*/
|
||||||
@ -777,7 +679,11 @@ void or_(IntT &destination, IntT source, ContextT &context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename ContextT>
|
template <typename IntT, typename ContextT>
|
||||||
void xor_(IntT &destination, IntT source, ContextT &context) {
|
void xor_(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
read_t<IntT> source,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
DEST ← DEST XOR SRC;
|
DEST ← DEST XOR SRC;
|
||||||
*/
|
*/
|
||||||
@ -792,7 +698,10 @@ void xor_(IntT &destination, IntT source, ContextT &context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename ContextT>
|
template <typename IntT, typename ContextT>
|
||||||
void neg(IntT &destination, ContextT &context) {
|
void neg(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
IF DEST = 0
|
IF DEST = 0
|
||||||
THEN CF ← 0
|
THEN CF ← 0
|
||||||
@ -814,35 +723,49 @@ void neg(IntT &destination, ContextT &context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT>
|
template <typename IntT>
|
||||||
void not_(IntT &destination) {
|
void not_(
|
||||||
|
modify_t<IntT> destination
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
DEST ← NOT DEST;
|
DEST ← NOT DEST;
|
||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
Flags affected: none.
|
Flags affected: none.
|
||||||
*/
|
*/
|
||||||
destination = ~destination;
|
destination = ~destination;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename ContextT>
|
template <typename IntT, typename ContextT>
|
||||||
void call_relative(IntT offset, ContextT &context) {
|
void call_relative(
|
||||||
|
IntT offset,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
push<uint16_t, false>(context.registers.ip(), context);
|
push<uint16_t, false>(context.registers.ip(), context);
|
||||||
context.flow_controller.jump(context.registers.ip() + offset);
|
context.flow_controller.jump(context.registers.ip() + offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename ContextT>
|
template <typename IntT, typename ContextT>
|
||||||
void call_absolute(IntT target, ContextT &context) {
|
void call_absolute(
|
||||||
|
read_t<IntT> target,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
push<uint16_t, false>(context.registers.ip(), context);
|
push<uint16_t, false>(context.registers.ip(), context);
|
||||||
context.flow_controller.jump(target);
|
context.flow_controller.jump(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename ContextT>
|
template <typename IntT, typename ContextT>
|
||||||
void jump_absolute(IntT target, ContextT &context) {
|
void jump_absolute(
|
||||||
|
read_t<IntT> target,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
context.flow_controller.jump(target);
|
context.flow_controller.jump(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename InstructionT, typename ContextT>
|
template <typename InstructionT, typename ContextT>
|
||||||
void call_far(InstructionT &instruction, ContextT &context) {
|
void call_far(
|
||||||
|
InstructionT &instruction,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
// TODO: eliminate 16-bit assumption below.
|
// TODO: eliminate 16-bit assumption below.
|
||||||
const Source source_segment = instruction.data_segment();
|
const Source source_segment = instruction.data_segment();
|
||||||
context.memory.preauthorise_stack_write(sizeof(uint16_t) * 2);
|
context.memory.preauthorise_stack_write(sizeof(uint16_t) * 2);
|
||||||
@ -881,7 +804,10 @@ void call_far(InstructionT &instruction, ContextT &context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename InstructionT, typename ContextT>
|
template <typename InstructionT, typename ContextT>
|
||||||
void jump_far(InstructionT &instruction, ContextT &context) {
|
void jump_far(
|
||||||
|
InstructionT &instruction,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
// TODO: eliminate 16-bit assumption below.
|
// TODO: eliminate 16-bit assumption below.
|
||||||
uint16_t source_address = 0;
|
uint16_t source_address = 0;
|
||||||
const auto pointer = instruction.destination();
|
const auto pointer = instruction.destination();
|
||||||
@ -910,7 +836,9 @@ void jump_far(InstructionT &instruction, ContextT &context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename ContextT>
|
template <typename ContextT>
|
||||||
void iret(ContextT &context) {
|
void iret(
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
// TODO: all modes other than 16-bit real mode.
|
// TODO: all modes other than 16-bit real mode.
|
||||||
context.memory.preauthorise_stack_read(sizeof(uint16_t) * 3);
|
context.memory.preauthorise_stack_read(sizeof(uint16_t) * 3);
|
||||||
const auto ip = pop<uint16_t, true>(context);
|
const auto ip = pop<uint16_t, true>(context);
|
||||||
@ -920,14 +848,20 @@ void iret(ContextT &context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename InstructionT, typename ContextT>
|
template <typename InstructionT, typename ContextT>
|
||||||
void ret_near(InstructionT instruction, ContextT &context) {
|
void ret_near(
|
||||||
|
InstructionT instruction,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
const auto ip = pop<uint16_t, false>(context);
|
const auto ip = pop<uint16_t, false>(context);
|
||||||
context.registers.sp() += instruction.operand();
|
context.registers.sp() += instruction.operand();
|
||||||
context.flow_controller.jump(ip);
|
context.flow_controller.jump(ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename InstructionT, typename ContextT>
|
template <typename InstructionT, typename ContextT>
|
||||||
void ret_far(InstructionT instruction, ContextT &context) {
|
void ret_far(
|
||||||
|
InstructionT instruction,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
context.memory.preauthorise_stack_read(sizeof(uint16_t) * 2);
|
context.memory.preauthorise_stack_read(sizeof(uint16_t) * 2);
|
||||||
const auto ip = pop<uint16_t, true>(context);
|
const auto ip = pop<uint16_t, true>(context);
|
||||||
const auto cs = pop<uint16_t, true>(context);
|
const auto cs = pop<uint16_t, true>(context);
|
||||||
@ -937,8 +871,8 @@ void ret_far(InstructionT instruction, ContextT &context) {
|
|||||||
|
|
||||||
template <Source selector, typename InstructionT, typename ContextT>
|
template <Source selector, typename InstructionT, typename ContextT>
|
||||||
void ld(
|
void ld(
|
||||||
InstructionT &instruction,
|
const InstructionT &instruction,
|
||||||
uint16_t &destination,
|
write_t<uint16_t> destination,
|
||||||
ContextT &context
|
ContextT &context
|
||||||
) {
|
) {
|
||||||
const auto pointer = instruction.source();
|
const auto pointer = instruction.source();
|
||||||
@ -957,7 +891,7 @@ void ld(
|
|||||||
template <typename IntT, typename InstructionT, typename ContextT>
|
template <typename IntT, typename InstructionT, typename ContextT>
|
||||||
void lea(
|
void lea(
|
||||||
const InstructionT &instruction,
|
const InstructionT &instruction,
|
||||||
IntT &destination,
|
write_t<IntT> destination,
|
||||||
ContextT &context
|
ContextT &context
|
||||||
) {
|
) {
|
||||||
// TODO: address size.
|
// TODO: address size.
|
||||||
@ -978,19 +912,27 @@ void xlat(
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT>
|
template <typename IntT>
|
||||||
void mov(IntT &destination, IntT source) {
|
void mov(
|
||||||
|
write_t<IntT> destination,
|
||||||
|
read_t<IntT> source
|
||||||
|
) {
|
||||||
destination = source;
|
destination = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ContextT>
|
template <typename ContextT>
|
||||||
void into(ContextT &context) {
|
void into(
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
if(context.flags.template flag<Flag::Overflow>()) {
|
if(context.flags.template flag<Flag::Overflow>()) {
|
||||||
interrupt(Interrupt::OnOverflow, context);
|
interrupt(Interrupt::OnOverflow, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ContextT>
|
template <typename ContextT>
|
||||||
void sahf(uint8_t &ah, ContextT &context) {
|
void sahf(
|
||||||
|
uint8_t &ah,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
EFLAGS(SF:ZF:0:AF:0:PF:1:CF) ← AH;
|
EFLAGS(SF:ZF:0:AF:0:PF:1:CF) ← AH;
|
||||||
*/
|
*/
|
||||||
@ -1002,7 +944,10 @@ void sahf(uint8_t &ah, ContextT &context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename ContextT>
|
template <typename ContextT>
|
||||||
void lahf(uint8_t &ah, ContextT &context) {
|
void lahf(
|
||||||
|
uint8_t &ah,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
AH ← EFLAGS(SF:ZF:0:AF:0:PF:1:CF);
|
AH ← EFLAGS(SF:ZF:0:AF:0:PF:1:CF);
|
||||||
*/
|
*/
|
||||||
@ -1016,7 +961,9 @@ void lahf(uint8_t &ah, ContextT &context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT>
|
template <typename IntT>
|
||||||
void cbw(IntT &ax) {
|
void cbw(
|
||||||
|
IntT &ax
|
||||||
|
) {
|
||||||
constexpr IntT test_bit = 1 << (sizeof(IntT) * 4 - 1);
|
constexpr IntT test_bit = 1 << (sizeof(IntT) * 4 - 1);
|
||||||
constexpr IntT low_half = (1 << (sizeof(IntT) * 4)) - 1;
|
constexpr IntT low_half = (1 << (sizeof(IntT) * 4)) - 1;
|
||||||
|
|
||||||
@ -1028,7 +975,10 @@ void cbw(IntT &ax) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT>
|
template <typename IntT>
|
||||||
void cwd(IntT &dx, IntT ax) {
|
void cwd(
|
||||||
|
IntT &dx,
|
||||||
|
IntT ax
|
||||||
|
) {
|
||||||
dx = ax & Numeric::top_bit<IntT>() ? IntT(~0) : IntT(0);
|
dx = ax & Numeric::top_bit<IntT>() ? IntT(~0) : IntT(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1051,19 +1001,29 @@ void cmc(ContextT &context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename ContextT>
|
template <typename ContextT>
|
||||||
void salc(uint8_t &al, ContextT &context) {
|
void salc(
|
||||||
|
uint8_t &al,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
al = context.flags.template flag<Flag::Carry>() ? 0xff : 0x00;
|
al = context.flags.template flag<Flag::Carry>() ? 0xff : 0x00;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename ContextT>
|
template <typename IntT, typename ContextT>
|
||||||
void setmo(IntT &destination, ContextT &context) {
|
void setmo(
|
||||||
destination = ~0;
|
write_t<IntT> destination,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
const auto result = destination = ~0;
|
||||||
context.flags.template set_from<Flag::Carry, Flag::AuxiliaryCarry, Flag::Overflow>(0);
|
context.flags.template set_from<Flag::Carry, Flag::AuxiliaryCarry, Flag::Overflow>(0);
|
||||||
context.flags.template set_from<IntT, Flag::Sign, Flag::Zero, Flag::ParityOdd>(destination);
|
context.flags.template set_from<IntT, Flag::Sign, Flag::Zero, Flag::ParityOdd>(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename ContextT>
|
template <typename IntT, typename ContextT>
|
||||||
void rcl(IntT &destination, uint8_t count, ContextT &context) {
|
void rcl(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
uint8_t count,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
(* RCL and RCR instructions *)
|
(* RCL and RCR instructions *)
|
||||||
SIZE ← OperandSize
|
SIZE ← OperandSize
|
||||||
@ -1119,7 +1079,11 @@ void rcl(IntT &destination, uint8_t count, ContextT &context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename ContextT>
|
template <typename IntT, typename ContextT>
|
||||||
void rcr(IntT &destination, uint8_t count, ContextT &context) {
|
void rcr(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
uint8_t count,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
(* RCR instruction operation *)
|
(* RCR instruction operation *)
|
||||||
IF COUNT = 1
|
IF COUNT = 1
|
||||||
@ -1161,7 +1125,11 @@ void rcr(IntT &destination, uint8_t count, ContextT &context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename ContextT>
|
template <typename IntT, typename ContextT>
|
||||||
void rol(IntT &destination, uint8_t count, ContextT &context) {
|
void rol(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
uint8_t count,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
(* ROL and ROR instructions *)
|
(* ROL and ROR instructions *)
|
||||||
SIZE ← OperandSize
|
SIZE ← OperandSize
|
||||||
@ -1208,7 +1176,11 @@ void rol(IntT &destination, uint8_t count, ContextT &context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename ContextT>
|
template <typename IntT, typename ContextT>
|
||||||
void ror(IntT &destination, uint8_t count, ContextT &context) {
|
void ror(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
uint8_t count,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
(* ROL and ROR instructions *)
|
(* ROL and ROR instructions *)
|
||||||
SIZE ← OperandSize
|
SIZE ← OperandSize
|
||||||
@ -1311,7 +1283,11 @@ void ror(IntT &destination, uint8_t count, ContextT &context) {
|
|||||||
For a non-zero count, the AF flag is undefined.
|
For a non-zero count, the AF flag is undefined.
|
||||||
*/
|
*/
|
||||||
template <typename IntT, typename ContextT>
|
template <typename IntT, typename ContextT>
|
||||||
void sal(IntT &destination, uint8_t count, ContextT &context) {
|
void sal(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
uint8_t count,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
switch(count) {
|
switch(count) {
|
||||||
case 0: return;
|
case 0: return;
|
||||||
case Numeric::bit_size<IntT>():
|
case Numeric::bit_size<IntT>():
|
||||||
@ -1338,7 +1314,11 @@ void sal(IntT &destination, uint8_t count, ContextT &context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename ContextT>
|
template <typename IntT, typename ContextT>
|
||||||
void sar(IntT &destination, uint8_t count, ContextT &context) {
|
void sar(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
uint8_t count,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
if(!count) {
|
if(!count) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1357,7 +1337,11 @@ void sar(IntT &destination, uint8_t count, ContextT &context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename ContextT>
|
template <typename IntT, typename ContextT>
|
||||||
void shr(IntT &destination, uint8_t count, ContextT &context) {
|
void shr(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
uint8_t count,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
if(!count) {
|
if(!count) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1378,23 +1362,32 @@ void shr(IntT &destination, uint8_t count, ContextT &context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename ContextT>
|
template <typename ContextT>
|
||||||
void popf(ContextT &context) {
|
void popf(
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
context.flags.set(pop<uint16_t, false>(context));
|
context.flags.set(pop<uint16_t, false>(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ContextT>
|
template <typename ContextT>
|
||||||
void pushf(ContextT &context) {
|
void pushf(
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
uint16_t value = context.flags.get();
|
uint16_t value = context.flags.get();
|
||||||
push<uint16_t, false>(value, context);
|
push<uint16_t, false>(value, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename AddressT, Repetition repetition>
|
template <typename AddressT, Repetition repetition>
|
||||||
bool repetition_over(const AddressT &eCX) {
|
bool repetition_over(
|
||||||
|
const AddressT &eCX
|
||||||
|
) {
|
||||||
return repetition != Repetition::None && !eCX;
|
return repetition != Repetition::None && !eCX;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename AddressT, Repetition repetition, typename ContextT>
|
template <typename AddressT, Repetition repetition, typename ContextT>
|
||||||
void repeat(AddressT &eCX, ContextT &context) {
|
void repeat(
|
||||||
|
AddressT &eCX,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
if(
|
if(
|
||||||
repetition == Repetition::None || // No repetition => stop.
|
repetition == Repetition::None || // No repetition => stop.
|
||||||
!(--eCX) // [e]cx is zero after being decremented => stop.
|
!(--eCX) // [e]cx is zero after being decremented => stop.
|
||||||
@ -1411,7 +1404,13 @@ void repeat(AddressT &eCX, ContextT &context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename IntT, typename AddressT, Repetition repetition, typename InstructionT, typename ContextT>
|
template <typename IntT, typename AddressT, Repetition repetition, typename InstructionT, typename ContextT>
|
||||||
void cmps(const InstructionT &instruction, AddressT &eCX, AddressT &eSI, AddressT &eDI, ContextT &context) {
|
void cmps(
|
||||||
|
const InstructionT &instruction,
|
||||||
|
AddressT &eCX,
|
||||||
|
AddressT &eSI,
|
||||||
|
AddressT &eDI,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
if(repetition_over<AddressT, repetition>(eCX)) {
|
if(repetition_over<AddressT, repetition>(eCX)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1421,7 +1420,7 @@ void cmps(const InstructionT &instruction, AddressT &eCX, AddressT &eSI, Address
|
|||||||
eSI += context.flags.template direction<AddressT>() * sizeof(IntT);
|
eSI += context.flags.template direction<AddressT>() * sizeof(IntT);
|
||||||
eDI += context.flags.template direction<AddressT>() * sizeof(IntT);
|
eDI += context.flags.template direction<AddressT>() * sizeof(IntT);
|
||||||
|
|
||||||
Primitive::sub<false, false>(lhs, rhs, context);
|
Primitive::sub<false, AccessType::Read, IntT>(lhs, rhs, context);
|
||||||
|
|
||||||
repeat<AddressT, repetition>(eCX, context);
|
repeat<AddressT, repetition>(eCX, context);
|
||||||
}
|
}
|
||||||
@ -1435,7 +1434,7 @@ void scas(AddressT &eCX, AddressT &eDI, IntT &eAX, ContextT &context) {
|
|||||||
const IntT rhs = context.memory.template access<IntT, AccessType::Read>(Source::ES, eDI);
|
const IntT rhs = context.memory.template access<IntT, AccessType::Read>(Source::ES, eDI);
|
||||||
eDI += context.flags.template direction<AddressT>() * sizeof(IntT);
|
eDI += context.flags.template direction<AddressT>() * sizeof(IntT);
|
||||||
|
|
||||||
Primitive::sub<false, false>(eAX, rhs, context);
|
Primitive::sub<false, AccessType::Read, IntT>(eAX, rhs, context);
|
||||||
|
|
||||||
repeat<AddressT, repetition>(eCX, context);
|
repeat<AddressT, repetition>(eCX, context);
|
||||||
}
|
}
|
||||||
@ -1537,8 +1536,8 @@ template <
|
|||||||
//
|
//
|
||||||
// (though GCC offers C++20 syntax as an extension, and Clang seems to follow along, so maybe I'm overthinking)
|
// (though GCC offers C++20 syntax as an extension, and Clang seems to follow along, so maybe I'm overthinking)
|
||||||
IntT immediate;
|
IntT immediate;
|
||||||
const auto source_r = [&]() -> IntT& {
|
const auto source_r = [&]() -> read_t<IntT> {
|
||||||
return *resolve<IntT, AccessType::Read>(
|
return resolve<IntT, AccessType::Read>(
|
||||||
instruction,
|
instruction,
|
||||||
instruction.source().source(),
|
instruction.source().source(),
|
||||||
instruction.source(),
|
instruction.source(),
|
||||||
@ -1546,8 +1545,8 @@ template <
|
|||||||
nullptr,
|
nullptr,
|
||||||
&immediate);
|
&immediate);
|
||||||
};
|
};
|
||||||
const auto source_rmw = [&]() -> IntT& {
|
const auto source_rmw = [&]() -> modify_t<IntT> {
|
||||||
return *resolve<IntT, AccessType::ReadModifyWrite>(
|
return resolve<IntT, AccessType::ReadModifyWrite>(
|
||||||
instruction,
|
instruction,
|
||||||
instruction.source().source(),
|
instruction.source().source(),
|
||||||
instruction.source(),
|
instruction.source(),
|
||||||
@ -1555,8 +1554,8 @@ template <
|
|||||||
nullptr,
|
nullptr,
|
||||||
&immediate);
|
&immediate);
|
||||||
};
|
};
|
||||||
const auto destination_r = [&]() -> IntT& {
|
const auto destination_r = [&]() -> read_t<IntT> {
|
||||||
return *resolve<IntT, AccessType::Read>(
|
return resolve<IntT, AccessType::Read>(
|
||||||
instruction,
|
instruction,
|
||||||
instruction.destination().source(),
|
instruction.destination().source(),
|
||||||
instruction.destination(),
|
instruction.destination(),
|
||||||
@ -1564,8 +1563,8 @@ template <
|
|||||||
nullptr,
|
nullptr,
|
||||||
&immediate);
|
&immediate);
|
||||||
};
|
};
|
||||||
const auto destination_w = [&]() -> IntT& {
|
const auto destination_w = [&]() -> write_t<IntT> {
|
||||||
return *resolve<IntT, AccessType::Write>(
|
return resolve<IntT, AccessType::Write>(
|
||||||
instruction,
|
instruction,
|
||||||
instruction.destination().source(),
|
instruction.destination().source(),
|
||||||
instruction.destination(),
|
instruction.destination(),
|
||||||
@ -1573,8 +1572,8 @@ template <
|
|||||||
nullptr,
|
nullptr,
|
||||||
&immediate);
|
&immediate);
|
||||||
};
|
};
|
||||||
const auto destination_rmw = [&]() -> IntT& {
|
const auto destination_rmw = [&]() -> modify_t<IntT> {
|
||||||
return *resolve<IntT, AccessType::ReadModifyWrite>(
|
return resolve<IntT, AccessType::ReadModifyWrite>(
|
||||||
instruction,
|
instruction,
|
||||||
instruction.destination().source(),
|
instruction.destination().source(),
|
||||||
instruction.destination(),
|
instruction.destination(),
|
||||||
@ -1672,39 +1671,45 @@ template <
|
|||||||
case Operation::HLT: context.flow_controller.halt(); return;
|
case Operation::HLT: context.flow_controller.halt(); return;
|
||||||
case Operation::WAIT: context.flow_controller.wait(); return;
|
case Operation::WAIT: context.flow_controller.wait(); return;
|
||||||
|
|
||||||
case Operation::ADC: Primitive::add<true>(destination_rmw(), source_r(), context); break;
|
case Operation::ADC: Primitive::add<true, IntT>(destination_rmw(), source_r(), context); break;
|
||||||
case Operation::ADD: Primitive::add<false>(destination_rmw(), source_r(), context); break;
|
case Operation::ADD: Primitive::add<false, IntT>(destination_rmw(), source_r(), context); break;
|
||||||
case Operation::SBB: Primitive::sub<true, true>(destination_rmw(), source_r(), context); break;
|
case Operation::SBB:
|
||||||
case Operation::SUB: Primitive::sub<false, true>(destination_rmw(), source_r(), context); break;
|
Primitive::sub<true, AccessType::ReadModifyWrite, IntT>(destination_rmw(), source_r(), context);
|
||||||
case Operation::CMP: Primitive::sub<false, false>(destination_r(), source_r(), context); return;
|
break;
|
||||||
case Operation::TEST: Primitive::test(destination_r(), source_r(), context); return;
|
case Operation::SUB:
|
||||||
|
Primitive::sub<false, AccessType::ReadModifyWrite, IntT>(destination_rmw(), source_r(), context);
|
||||||
|
break;
|
||||||
|
case Operation::CMP:
|
||||||
|
Primitive::sub<false, AccessType::Read, IntT>(destination_r(), source_r(), context);
|
||||||
|
return;
|
||||||
|
case Operation::TEST: Primitive::test<IntT>(destination_r(), source_r(), context); return;
|
||||||
|
|
||||||
case Operation::MUL: Primitive::mul(pair_high(), pair_low(), source_r(), context); return;
|
case Operation::MUL: Primitive::mul<IntT>(pair_high(), pair_low(), source_r(), context); return;
|
||||||
case Operation::IMUL_1: Primitive::imul(pair_high(), pair_low(), source_r(), context); return;
|
case Operation::IMUL_1: Primitive::imul<IntT>(pair_high(), pair_low(), source_r(), context); return;
|
||||||
case Operation::DIV: Primitive::div(pair_high(), pair_low(), source_r(), context); return;
|
case Operation::DIV: Primitive::div<IntT>(pair_high(), pair_low(), source_r(), context); return;
|
||||||
case Operation::IDIV: Primitive::idiv(pair_high(), pair_low(), source_r(), context); return;
|
case Operation::IDIV: Primitive::idiv<IntT>(pair_high(), pair_low(), source_r(), context); return;
|
||||||
|
|
||||||
case Operation::INC: Primitive::inc(destination_rmw(), context); break;
|
case Operation::INC: Primitive::inc<IntT>(destination_rmw(), context); break;
|
||||||
case Operation::DEC: Primitive::dec(destination_rmw(), context); break;
|
case Operation::DEC: Primitive::dec<IntT>(destination_rmw(), context); break;
|
||||||
|
|
||||||
case Operation::AND: Primitive::and_(destination_rmw(), source_r(), context); break;
|
case Operation::AND: Primitive::and_<IntT>(destination_rmw(), source_r(), context); break;
|
||||||
case Operation::OR: Primitive::or_(destination_rmw(), source_r(), context); break;
|
case Operation::OR: Primitive::or_<IntT>(destination_rmw(), source_r(), context); break;
|
||||||
case Operation::XOR: Primitive::xor_(destination_rmw(), source_r(), context); break;
|
case Operation::XOR: Primitive::xor_<IntT>(destination_rmw(), source_r(), context); break;
|
||||||
case Operation::NEG: Primitive::neg(source_rmw(), context); break; // TODO: should be a destination.
|
case Operation::NEG: Primitive::neg<IntT>(source_rmw(), context); break; // TODO: should be a destination.
|
||||||
case Operation::NOT: Primitive::not_(source_rmw()); break; // TODO: should be a destination.
|
case Operation::NOT: Primitive::not_<IntT>(source_rmw()); break; // TODO: should be a destination.
|
||||||
|
|
||||||
case Operation::CALLrel: Primitive::call_relative(instruction.displacement(), context); return;
|
case Operation::CALLrel: Primitive::call_relative<AddressT>(instruction.displacement(), context); return;
|
||||||
case Operation::CALLabs: Primitive::call_absolute(destination_r(), context); return;
|
case Operation::CALLabs: Primitive::call_absolute<IntT>(destination_r(), context); return;
|
||||||
case Operation::CALLfar: Primitive::call_far(instruction, context); return;
|
case Operation::CALLfar: Primitive::call_far(instruction, context); return;
|
||||||
|
|
||||||
case Operation::JMPrel: jcc(true); return;
|
case Operation::JMPrel: jcc(true); return;
|
||||||
case Operation::JMPabs: Primitive::jump_absolute(destination_r(), context); return;
|
case Operation::JMPabs: Primitive::jump_absolute<IntT>(destination_r(), context); return;
|
||||||
case Operation::JMPfar: Primitive::jump_far(instruction, context); return;
|
case Operation::JMPfar: Primitive::jump_far(instruction, context); return;
|
||||||
|
|
||||||
case Operation::JCXZ: jcc(!eCX()); return;
|
case Operation::JCXZ: jcc(!eCX()); return;
|
||||||
case Operation::LOOP: Primitive::loop(eCX(), instruction.offset(), context); return;
|
case Operation::LOOP: Primitive::loop<AddressT>(eCX(), instruction.offset(), context); return;
|
||||||
case Operation::LOOPE: Primitive::loope(eCX(), instruction.offset(), context); return;
|
case Operation::LOOPE: Primitive::loope<AddressT>(eCX(), instruction.offset(), context); return;
|
||||||
case Operation::LOOPNE: Primitive::loopne(eCX(), instruction.offset(), context); return;
|
case Operation::LOOPNE: Primitive::loopne<AddressT>(eCX(), instruction.offset(), context); return;
|
||||||
|
|
||||||
case Operation::IRET: Primitive::iret(context); return;
|
case Operation::IRET: Primitive::iret(context); return;
|
||||||
case Operation::RETnear: Primitive::ret_near(instruction, context); return;
|
case Operation::RETnear: Primitive::ret_near(instruction, context); return;
|
||||||
@ -1719,33 +1724,33 @@ template <
|
|||||||
case Operation::LDS: if constexpr (data_size == DataSize::Word) Primitive::ld<Source::DS>(instruction, destination_w(), context); return;
|
case Operation::LDS: if constexpr (data_size == DataSize::Word) Primitive::ld<Source::DS>(instruction, destination_w(), context); return;
|
||||||
case Operation::LES: if constexpr (data_size == DataSize::Word) Primitive::ld<Source::ES>(instruction, destination_w(), context); return;
|
case Operation::LES: if constexpr (data_size == DataSize::Word) Primitive::ld<Source::ES>(instruction, destination_w(), context); return;
|
||||||
|
|
||||||
case Operation::LEA: Primitive::lea(instruction, destination_w(), context); return;
|
case Operation::LEA: Primitive::lea<IntT>(instruction, destination_w(), context); return;
|
||||||
case Operation::MOV: Primitive::mov(destination_w(), source_r()); break;
|
case Operation::MOV: Primitive::mov<IntT>(destination_w(), source_r()); break;
|
||||||
|
|
||||||
case Operation::JO: jcc(context.flags.template condition<Condition::Overflow>()); return;
|
case Operation::JO: jcc(context.flags.template condition<Condition::Overflow>()); return;
|
||||||
case Operation::JNO: jcc(!context.flags.template condition<Condition::Overflow>()); return;
|
case Operation::JNO: jcc(!context.flags.template condition<Condition::Overflow>()); return;
|
||||||
case Operation::JB: jcc(context.flags.template condition<Condition::Below>()); return;
|
case Operation::JB: jcc(context.flags.template condition<Condition::Below>()); return;
|
||||||
case Operation::JNB: jcc(!context.flags.template condition<Condition::Below>()); return;
|
case Operation::JNB: jcc(!context.flags.template condition<Condition::Below>()); return;
|
||||||
case Operation::JZ: jcc(context.flags.template condition<Condition::Zero>()); return;
|
case Operation::JZ: jcc(context.flags.template condition<Condition::Zero>()); return;
|
||||||
case Operation::JNZ: jcc(!context.flags.template condition<Condition::Zero>()); return;
|
case Operation::JNZ: jcc(!context.flags.template condition<Condition::Zero>()); return;
|
||||||
case Operation::JBE: jcc(context.flags.template condition<Condition::BelowOrEqual>()); return;
|
case Operation::JBE: jcc(context.flags.template condition<Condition::BelowOrEqual>()); return;
|
||||||
case Operation::JNBE: jcc(!context.flags.template condition<Condition::BelowOrEqual>()); return;
|
case Operation::JNBE: jcc(!context.flags.template condition<Condition::BelowOrEqual>()); return;
|
||||||
case Operation::JS: jcc(context.flags.template condition<Condition::Sign>()); return;
|
case Operation::JS: jcc(context.flags.template condition<Condition::Sign>()); return;
|
||||||
case Operation::JNS: jcc(!context.flags.template condition<Condition::Sign>()); return;
|
case Operation::JNS: jcc(!context.flags.template condition<Condition::Sign>()); return;
|
||||||
case Operation::JP: jcc(!context.flags.template condition<Condition::ParityOdd>()); return;
|
case Operation::JP: jcc(!context.flags.template condition<Condition::ParityOdd>()); return;
|
||||||
case Operation::JNP: jcc(context.flags.template condition<Condition::ParityOdd>()); return;
|
case Operation::JNP: jcc(context.flags.template condition<Condition::ParityOdd>()); return;
|
||||||
case Operation::JL: jcc(context.flags.template condition<Condition::Less>()); return;
|
case Operation::JL: jcc(context.flags.template condition<Condition::Less>()); return;
|
||||||
case Operation::JNL: jcc(!context.flags.template condition<Condition::Less>()); return;
|
case Operation::JNL: jcc(!context.flags.template condition<Condition::Less>()); return;
|
||||||
case Operation::JLE: jcc(context.flags.template condition<Condition::LessOrEqual>()); return;
|
case Operation::JLE: jcc(context.flags.template condition<Condition::LessOrEqual>()); return;
|
||||||
case Operation::JNLE: jcc(!context.flags.template condition<Condition::LessOrEqual>()); return;
|
case Operation::JNLE: jcc(!context.flags.template condition<Condition::LessOrEqual>()); return;
|
||||||
|
|
||||||
case Operation::RCL: Primitive::rcl(destination_rmw(), shift_count(), context); break;
|
case Operation::RCL: Primitive::rcl<IntT>(destination_rmw(), shift_count(), context); break;
|
||||||
case Operation::RCR: Primitive::rcr(destination_rmw(), shift_count(), context); break;
|
case Operation::RCR: Primitive::rcr<IntT>(destination_rmw(), shift_count(), context); break;
|
||||||
case Operation::ROL: Primitive::rol(destination_rmw(), shift_count(), context); break;
|
case Operation::ROL: Primitive::rol<IntT>(destination_rmw(), shift_count(), context); break;
|
||||||
case Operation::ROR: Primitive::ror(destination_rmw(), shift_count(), context); break;
|
case Operation::ROR: Primitive::ror<IntT>(destination_rmw(), shift_count(), context); break;
|
||||||
case Operation::SAL: Primitive::sal(destination_rmw(), shift_count(), context); break;
|
case Operation::SAL: Primitive::sal<IntT>(destination_rmw(), shift_count(), context); break;
|
||||||
case Operation::SAR: Primitive::sar(destination_rmw(), shift_count(), context); break;
|
case Operation::SAR: Primitive::sar<IntT>(destination_rmw(), shift_count(), context); break;
|
||||||
case Operation::SHR: Primitive::shr(destination_rmw(), shift_count(), context); break;
|
case Operation::SHR: Primitive::shr<IntT>(destination_rmw(), shift_count(), context); break;
|
||||||
|
|
||||||
case Operation::CLC: Primitive::clc(context); return;
|
case Operation::CLC: Primitive::clc(context); return;
|
||||||
case Operation::CLD: Primitive::cld(context); return;
|
case Operation::CLD: Primitive::cld(context); return;
|
||||||
@ -1755,12 +1760,12 @@ template <
|
|||||||
case Operation::STI: Primitive::sti(context); return;
|
case Operation::STI: Primitive::sti(context); return;
|
||||||
case Operation::CMC: Primitive::cmc(context); return;
|
case Operation::CMC: Primitive::cmc(context); return;
|
||||||
|
|
||||||
case Operation::XCHG: Primitive::xchg(destination_rmw(), source_rmw()); break;
|
case Operation::XCHG: Primitive::xchg<IntT>(destination_rmw(), source_rmw()); break;
|
||||||
|
|
||||||
case Operation::SALC: Primitive::salc(context.registers.al(), context); return;
|
case Operation::SALC: Primitive::salc(context.registers.al(), context); return;
|
||||||
case Operation::SETMO:
|
case Operation::SETMO:
|
||||||
if constexpr (ContextT::model == Model::i8086) {
|
if constexpr (ContextT::model == Model::i8086) {
|
||||||
Primitive::setmo(destination_w(), context);
|
Primitive::setmo<IntT>(destination_w(), context);
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
// TODO.
|
// TODO.
|
||||||
@ -1771,7 +1776,7 @@ template <
|
|||||||
// Test CL out here to avoid taking a reference to memory if
|
// Test CL out here to avoid taking a reference to memory if
|
||||||
// no write is going to occur.
|
// no write is going to occur.
|
||||||
if(context.registers.cl()) {
|
if(context.registers.cl()) {
|
||||||
Primitive::setmo(destination_w(), context);
|
Primitive::setmo<IntT>(destination_w(), context);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
@ -1785,7 +1790,10 @@ template <
|
|||||||
case Operation::XLAT: Primitive::xlat<AddressT>(instruction, context); return;
|
case Operation::XLAT: Primitive::xlat<AddressT>(instruction, context); return;
|
||||||
|
|
||||||
case Operation::POP: destination_w() = Primitive::pop<IntT, false>(context); break;
|
case Operation::POP: destination_w() = Primitive::pop<IntT, false>(context); break;
|
||||||
case Operation::PUSH: Primitive::push<IntT, false>(source_r(), context); break;
|
case Operation::PUSH:
|
||||||
|
Primitive::push<IntT, false>(source_rmw(), context); // PUSH SP modifies SP before pushing it;
|
||||||
|
// hence PUSH is sometimes read-modify-write.
|
||||||
|
break;
|
||||||
case Operation::POPF: Primitive::popf(context); break;
|
case Operation::POPF: Primitive::popf(context); break;
|
||||||
case Operation::PUSHF: Primitive::pushf(context); break;
|
case Operation::PUSHF: Primitive::pushf(context); break;
|
||||||
|
|
||||||
|
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>
|
||||||
|
typename Accessor<IntT, access>::type 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, AccessType::Read>(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, AccessType::Read>(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>
|
||||||
|
typename Accessor<IntT, access>::type 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 */
|
@ -506,7 +506,12 @@ std::string InstructionSet::x86::to_string(
|
|||||||
default: {
|
default: {
|
||||||
const int operands = max_displayed_operands(instruction.second.operation());
|
const int operands = max_displayed_operands(instruction.second.operation());
|
||||||
const bool displacement = has_displacement(instruction.second.operation());
|
const bool displacement = has_displacement(instruction.second.operation());
|
||||||
const bool print_first = operands > 1 && instruction.second.destination().source() != Source::None;
|
const bool print_first =
|
||||||
|
instruction.second.destination().source() != Source::None &&
|
||||||
|
(
|
||||||
|
operands > 1 ||
|
||||||
|
(operands > 0 && instruction.second.source().source() == Source::None)
|
||||||
|
);
|
||||||
if(print_first) {
|
if(print_first) {
|
||||||
operation += to_string(instruction.second.destination(), instruction.second, offset_length, immediate_length);
|
operation += to_string(instruction.second.destination(), instruction.second, offset_length, immediate_length);
|
||||||
}
|
}
|
||||||
|
@ -15,27 +15,6 @@
|
|||||||
|
|
||||||
namespace InstructionSet::x86 {
|
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,
|
|
||||||
PreauthorisedWrite,
|
|
||||||
};
|
|
||||||
|
|
||||||
template <
|
template <
|
||||||
Model model_,
|
Model model_,
|
||||||
typename FlowControllerT,
|
typename FlowControllerT,
|
||||||
|
@ -1150,6 +1150,8 @@
|
|||||||
42A5E8412ABBE16F00A0DD5D /* nop_test.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = nop_test.bin; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
42AD55312A0C4D5000ACE410 /* 68000Implementation.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 68000Implementation.hpp; sourceTree = "<group>"; };
|
||||||
@ -2328,6 +2330,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
42437B382ACF2798006DFED1 /* PerformImplementation.hpp */,
|
42437B382ACF2798006DFED1 /* PerformImplementation.hpp */,
|
||||||
|
42AA41242AF8893F0016751C /* Resolver.hpp */,
|
||||||
);
|
);
|
||||||
path = Implementation;
|
path = Implementation;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -4997,12 +5000,13 @@
|
|||||||
children = (
|
children = (
|
||||||
4BEDA3B925B25563000C2DBD /* Decoder.cpp */,
|
4BEDA3B925B25563000C2DBD /* Decoder.cpp */,
|
||||||
4B69DEB52AB79E4F0055B217 /* Instruction.cpp */,
|
4B69DEB52AB79E4F0055B217 /* Instruction.cpp */,
|
||||||
|
42AA41232AF888370016751C /* AccessType.hpp */,
|
||||||
4BEDA3B825B25563000C2DBD /* Decoder.hpp */,
|
4BEDA3B825B25563000C2DBD /* Decoder.hpp */,
|
||||||
|
42437B342ACF02A9006DFED1 /* Flags.hpp */,
|
||||||
4BEDA3DB25B2588F000C2DBD /* Instruction.hpp */,
|
4BEDA3DB25B2588F000C2DBD /* Instruction.hpp */,
|
||||||
42437B392AD07465006DFED1 /* Interrupts.hpp */,
|
42437B392AD07465006DFED1 /* Interrupts.hpp */,
|
||||||
4BE3C69527CBC540000EAD28 /* Model.hpp */,
|
4BE3C69527CBC540000EAD28 /* Model.hpp */,
|
||||||
42437B352ACF0AA2006DFED1 /* Perform.hpp */,
|
42437B352ACF0AA2006DFED1 /* Perform.hpp */,
|
||||||
42437B342ACF02A9006DFED1 /* Flags.hpp */,
|
|
||||||
42437B372ACF2798006DFED1 /* Implementation */,
|
42437B372ACF2798006DFED1 /* Implementation */,
|
||||||
);
|
);
|
||||||
path = x86;
|
path = x86;
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "NSData+dataWithContentsOfGZippedFile.h"
|
#include "NSData+dataWithContentsOfGZippedFile.h"
|
||||||
|
|
||||||
|
#include "../../../InstructionSets/x86/AccessType.hpp"
|
||||||
#include "../../../InstructionSets/x86/Decoder.hpp"
|
#include "../../../InstructionSets/x86/Decoder.hpp"
|
||||||
#include "../../../InstructionSets/x86/Perform.hpp"
|
#include "../../../InstructionSets/x86/Perform.hpp"
|
||||||
#include "../../../InstructionSets/x86/Flags.hpp"
|
#include "../../../InstructionSets/x86/Flags.hpp"
|
||||||
@ -97,17 +98,6 @@ struct Memory {
|
|||||||
public:
|
public:
|
||||||
using AccessType = InstructionSet::x86::AccessType;
|
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 &; };
|
|
||||||
template <typename IntT> struct ReturnType<IntT, AccessType::PreauthorisedWrite> { using type = IntT &; };
|
|
||||||
|
|
||||||
// Constructor.
|
// Constructor.
|
||||||
Memory(Registers ®isters) : registers_(registers) {
|
Memory(Registers ®isters) : registers_(registers) {
|
||||||
memory.resize(1024*1024);
|
memory.resize(1024*1024);
|
||||||
@ -127,7 +117,9 @@ struct Memory {
|
|||||||
tags[address] = Tag::AccessExpected;
|
tags[address] = Tag::AccessExpected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
// Preauthorisation call-ins.
|
// Preauthorisation call-ins.
|
||||||
|
//
|
||||||
void preauthorise_stack_write(uint32_t length) {
|
void preauthorise_stack_write(uint32_t length) {
|
||||||
uint16_t sp = registers_.sp_;
|
uint16_t sp = registers_.sp_;
|
||||||
while(length--) {
|
while(length--) {
|
||||||
@ -155,34 +147,19 @@ struct Memory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
// Access call-ins.
|
// Access call-ins.
|
||||||
|
//
|
||||||
|
|
||||||
// Accesses an address based on segment:offset.
|
// Accesses an address based on segment:offset.
|
||||||
template <typename IntT, AccessType type>
|
template <typename IntT, AccessType type>
|
||||||
typename ReturnType<IntT, type>::type &access(InstructionSet::x86::Source segment, uint32_t address) {
|
typename InstructionSet::x86::Accessor<IntT, type>::type access(InstructionSet::x86::Source segment, uint16_t offset) {
|
||||||
if constexpr (std::is_same_v<IntT, uint16_t>) {
|
return access<IntT, type>(segment, offset, Tag::Accessed);
|
||||||
// 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.
|
|
||||||
if(address == 0xffff) {
|
|
||||||
write_back_address_[0] = (segment_base(segment) + address) & 0xf'ffff;
|
|
||||||
write_back_address_[1] = (write_back_address_[0] - 65535) & 0xf'ffff;
|
|
||||||
write_back_value_ = memory[write_back_address_[0]] | (memory[write_back_address_[1]] << 8);
|
|
||||||
return write_back_value_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto &value = access<IntT, type>(segment, address, Tag::Accessed);
|
|
||||||
|
|
||||||
// If the CPU has indicated a write, it should be safe to fuzz the value now.
|
|
||||||
if(type == AccessType::Write) {
|
|
||||||
value = IntT(~0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accesses an address based on physical location.
|
// Accesses an address based on physical location.
|
||||||
template <typename IntT, AccessType type>
|
template <typename IntT, AccessType type>
|
||||||
typename ReturnType<IntT, type>::type &access(uint32_t address) {
|
typename InstructionSet::x86::Accessor<IntT, type>::type access(uint32_t address) {
|
||||||
return access<IntT, type>(address, Tag::Accessed);
|
return access<IntT, type>(address, Tag::Accessed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,6 +174,41 @@ struct Memory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Direct write.
|
||||||
|
//
|
||||||
|
template <typename IntT>
|
||||||
|
void preauthorised_write(InstructionSet::x86::Source segment, uint16_t offset, IntT value) {
|
||||||
|
if(!test_preauthorisation(address(segment, offset))) {
|
||||||
|
printf("Non-preauthorised access\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes can be written without further ado.
|
||||||
|
if constexpr (std::is_same_v<IntT, uint8_t>) {
|
||||||
|
memory[address(segment, offset) & 0xf'ffff] = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Words that straddle the segment end must be split in two.
|
||||||
|
if(offset == 0xffff) {
|
||||||
|
memory[address(segment, offset) & 0xf'ffff] = value & 0xff;
|
||||||
|
memory[address(segment, 0x0000) & 0xf'ffff] = value >> 8;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t target = address(segment, offset) & 0xf'ffff;
|
||||||
|
|
||||||
|
// Words that straddle the end of physical RAM must also be split in two.
|
||||||
|
if(target == 0xf'ffff) {
|
||||||
|
memory[0xf'ffff] = value & 0xff;
|
||||||
|
memory[0x0'0000] = value >> 8;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's safe just to write then.
|
||||||
|
*reinterpret_cast<uint16_t *>(&memory[target]) = value;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class Tag {
|
enum class Tag {
|
||||||
Seeded,
|
Seeded,
|
||||||
@ -236,46 +248,70 @@ struct Memory {
|
|||||||
return physical_address << 4;
|
return physical_address << 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t address(InstructionSet::x86::Source segment, uint16_t offset) {
|
||||||
|
return (segment_base(segment) + offset) & 0xf'ffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Entry point used by the flow controller so that it can mark up locations at which the flags were written,
|
// 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.
|
// so that defined-flag-only masks can be applied while verifying RAM contents.
|
||||||
template <typename IntT, AccessType type>
|
template <typename IntT, AccessType type>
|
||||||
typename ReturnType<IntT, type>::type &access(InstructionSet::x86::Source segment, uint16_t address, Tag tag) {
|
typename InstructionSet::x86::Accessor<IntT, type>::type access(InstructionSet::x86::Source segment, uint16_t offset, Tag tag) {
|
||||||
const uint32_t physical_address = (segment_base(segment) + address) & 0xf'ffff;
|
const uint32_t physical_address = address(segment, 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.
|
||||||
|
if(offset == 0xffff) {
|
||||||
|
return split_word<type>(physical_address, address(segment, 0), tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return access<IntT, type>(physical_address, tag);
|
return access<IntT, type>(physical_address, tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
// An additional entry point for the flow controller; on the original 8086 interrupt vectors aren't relative
|
// 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.
|
// to a selector, they're just at an absolute location.
|
||||||
template <typename IntT, AccessType type>
|
template <typename IntT, AccessType type>
|
||||||
typename ReturnType<IntT, type>::type &access(uint32_t address, Tag tag) {
|
typename InstructionSet::x86::Accessor<IntT, type>::type access(uint32_t address, Tag tag) {
|
||||||
if constexpr (type == AccessType::PreauthorisedRead || type == AccessType::PreauthorisedWrite) {
|
if constexpr (type == AccessType::PreauthorisedRead) {
|
||||||
if(!test_preauthorisation(address)) {
|
if(!test_preauthorisation(address)) {
|
||||||
printf("Non preauthorised access\n");
|
printf("Non preauthorised access\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for address wraparound
|
for(size_t c = 0; c < sizeof(IntT); c++) {
|
||||||
if(address > 0x10'0000 - sizeof(IntT)) {
|
tags[(address + c) & 0xf'ffff] = tag;
|
||||||
if constexpr (std::is_same_v<IntT, uint8_t>) {
|
|
||||||
address &= 0xf'ffff;
|
|
||||||
} else {
|
|
||||||
address &= 0xf'ffff;
|
|
||||||
if(address == 0xf'ffff) {
|
|
||||||
// This is a 16-bit access comprising the final byte in memory and the first.
|
|
||||||
write_back_address_[0] = address;
|
|
||||||
write_back_address_[1] = 0;
|
|
||||||
write_back_value_ = memory[write_back_address_[0]] | (memory[write_back_address_[1]] << 8);
|
|
||||||
return write_back_value_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(tags.find(address) == tags.end()) {
|
// Dispense with the single-byte case trivially.
|
||||||
printf("Access to unexpected RAM address\n");
|
if constexpr (std::is_same_v<IntT, uint8_t>) {
|
||||||
|
return memory[address];
|
||||||
|
} else if(address != 0xf'ffff) {
|
||||||
|
return *reinterpret_cast<IntT *>(&memory[address]);
|
||||||
|
} else {
|
||||||
|
return split_word<type>(address, 0, tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <AccessType type>
|
||||||
|
typename InstructionSet::x86::Accessor<uint16_t, type>::type
|
||||||
|
split_word(uint32_t low_address, uint32_t high_address, Tag tag) {
|
||||||
|
if constexpr (is_writeable(type)) {
|
||||||
|
write_back_address_[0] = low_address;
|
||||||
|
write_back_address_[1] = high_address;
|
||||||
|
tags[low_address] = tag;
|
||||||
|
tags[high_address] = tag;
|
||||||
|
|
||||||
|
// Prepopulate only if this is a modify.
|
||||||
|
if constexpr (type == AccessType::ReadModifyWrite) {
|
||||||
|
write_back_value_ = memory[write_back_address_[0]] | (memory[write_back_address_[1]] << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
return write_back_value_;
|
||||||
|
} else {
|
||||||
|
return memory[low_address] | (memory[high_address] << 8);
|
||||||
}
|
}
|
||||||
tags[address] = tag;
|
|
||||||
return *reinterpret_cast<IntT *>(&memory[address]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr uint32_t NoWriteBack = 0; // A low byte address of 0 can't require write-back.
|
static constexpr uint32_t NoWriteBack = 0; // A low byte address of 0 can't require write-back.
|
||||||
|
Loading…
Reference in New Issue
Block a user