mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-18 01:30:56 +00:00
Remove dead DataPointerResolver and extra-conditional version of source().
This commit is contained in:
parent
0c09c14baa
commit
3b62638b30
@ -1,325 +0,0 @@
|
|||||||
//
|
|
||||||
// DataPointerResolver.hpp
|
|
||||||
// Clock Signal
|
|
||||||
//
|
|
||||||
// Created by Thomas Harte on 24/02/2022.
|
|
||||||
// Copyright © 2022 Thomas Harte. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef DataPointerResolver_hpp
|
|
||||||
#define DataPointerResolver_hpp
|
|
||||||
|
|
||||||
#include "Instruction.hpp"
|
|
||||||
#include "Model.hpp"
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
namespace InstructionSet::x86 {
|
|
||||||
|
|
||||||
/// Unlike source, describes only registers, and breaks
|
|
||||||
/// them down by conventional name — so AL, AH, AX and EAX are all
|
|
||||||
/// listed separately and uniquely, rather than being eAX+size or
|
|
||||||
/// eSPorAH with a size of 1.
|
|
||||||
enum class Register: uint8_t {
|
|
||||||
// 8-bit registers.
|
|
||||||
AL, AH,
|
|
||||||
CL, CH,
|
|
||||||
DL, DH,
|
|
||||||
BL, BH,
|
|
||||||
|
|
||||||
// 16-bit registers.
|
|
||||||
AX, CX, DX, BX,
|
|
||||||
SP, BP, SI, DI,
|
|
||||||
ES, CS, SS, DS,
|
|
||||||
FS, GS,
|
|
||||||
|
|
||||||
// 32-bit registers.
|
|
||||||
EAX, ECX, EDX, EBX,
|
|
||||||
ESP, EBP, ESI, EDI,
|
|
||||||
|
|
||||||
//
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @returns @c true if @c r is the same size as @c DataT; @c false otherwise.
|
|
||||||
/// @discussion Provided primarily to aid in asserts; if the decoder and resolver are both
|
|
||||||
/// working then it shouldn't be necessary to test this in register files.
|
|
||||||
template <typename DataT> constexpr bool is_sized(Register r) {
|
|
||||||
static_assert(sizeof(DataT) == 4 || sizeof(DataT) == 2 || sizeof(DataT) == 1);
|
|
||||||
|
|
||||||
if constexpr (sizeof(DataT) == 4) {
|
|
||||||
return r >= Register::EAX && r < Register::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
if constexpr (sizeof(DataT) == 2) {
|
|
||||||
return r >= Register::AX && r < Register::EAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
if constexpr (sizeof(DataT) == 1) {
|
|
||||||
return r >= Register::AL && r < Register::AX;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @returns the proper @c Register given @c source and data of size @c sizeof(DataT),
|
|
||||||
/// or Register::None if no such register exists (e.g. asking for a 32-bit version of CS).
|
|
||||||
template <typename DataT> constexpr Register register_for_source(Source source) {
|
|
||||||
static_assert(sizeof(DataT) == 4 || sizeof(DataT) == 2 || sizeof(DataT) == 1);
|
|
||||||
|
|
||||||
if constexpr (sizeof(DataT) == 4) {
|
|
||||||
switch(source) {
|
|
||||||
case Source::eAX: return Register::EAX;
|
|
||||||
case Source::eCX: return Register::ECX;
|
|
||||||
case Source::eDX: return Register::EDX;
|
|
||||||
case Source::eBX: return Register::EBX;
|
|
||||||
case Source::eSPorAH: return Register::ESP;
|
|
||||||
case Source::eBPorCH: return Register::EBP;
|
|
||||||
case Source::eSIorDH: return Register::ESI;
|
|
||||||
case Source::eDIorBH: return Register::EDI;
|
|
||||||
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if constexpr (sizeof(DataT) == 2) {
|
|
||||||
switch(source) {
|
|
||||||
case Source::eAX: return Register::AX;
|
|
||||||
case Source::eCX: return Register::CX;
|
|
||||||
case Source::eDX: return Register::DX;
|
|
||||||
case Source::eBX: return Register::BX;
|
|
||||||
case Source::eSPorAH: return Register::SP;
|
|
||||||
case Source::eBPorCH: return Register::BP;
|
|
||||||
case Source::eSIorDH: return Register::SI;
|
|
||||||
case Source::eDIorBH: return Register::DI;
|
|
||||||
case Source::ES: return Register::ES;
|
|
||||||
case Source::CS: return Register::CS;
|
|
||||||
case Source::SS: return Register::SS;
|
|
||||||
case Source::DS: return Register::DS;
|
|
||||||
case Source::FS: return Register::FS;
|
|
||||||
case Source::GS: return Register::GS;
|
|
||||||
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if constexpr (sizeof(DataT) == 1) {
|
|
||||||
switch(source) {
|
|
||||||
case Source::eAX: return Register::AL;
|
|
||||||
case Source::eCX: return Register::CL;
|
|
||||||
case Source::eDX: return Register::DL;
|
|
||||||
case Source::eBX: return Register::BL;
|
|
||||||
case Source::eSPorAH: return Register::AH;
|
|
||||||
case Source::eBPorCH: return Register::CH;
|
|
||||||
case Source::eSIorDH: return Register::DH;
|
|
||||||
case Source::eDIorBH: return Register::BH;
|
|
||||||
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Register::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads from or writes to the source or target identified by a DataPointer, relying upon two user-supplied classes:
|
|
||||||
///
|
|
||||||
/// * a register bank; and
|
|
||||||
/// * a memory pool.
|
|
||||||
///
|
|
||||||
/// The register bank should implement:
|
|
||||||
/// * `template<typename DataT, Register> DataT read()` and
|
|
||||||
/// * `template<typename DataT, Register> void write(DataT)`.
|
|
||||||
///
|
|
||||||
/// Which will be called only with registers and data types that are appropriate to the @c model.
|
|
||||||
///
|
|
||||||
/// The memory pool should implement `template<typename DataT> DataT read(Source segment, uint32_t address)` and
|
|
||||||
/// `template<typename DataT> void write(Source segment, uint32_t address, DataT value)`.
|
|
||||||
template <Model model, typename RegistersT, typename MemoryT> class DataPointerResolver {
|
|
||||||
public:
|
|
||||||
public:
|
|
||||||
/// Reads the data pointed to by @c pointer, referencing @c instruction, @c memory and @c registers as necessary.
|
|
||||||
template <typename DataT> static DataT read(
|
|
||||||
RegistersT ®isters,
|
|
||||||
MemoryT &memory,
|
|
||||||
const Instruction<is_32bit(model)> &instruction,
|
|
||||||
DataPointer pointer);
|
|
||||||
|
|
||||||
/// Writes @c value to the data pointed to by @c pointer, referencing @c instruction, @c memory and @c registers as necessary.
|
|
||||||
template <typename DataT> static void write(
|
|
||||||
RegistersT ®isters,
|
|
||||||
MemoryT &memory,
|
|
||||||
const Instruction<is_32bit(model)> &instruction,
|
|
||||||
DataPointer pointer,
|
|
||||||
DataT value);
|
|
||||||
|
|
||||||
/// Computes the effective address of @c pointer including any displacement applied by @c instruction.
|
|
||||||
/// @c pointer must be of type Source::Indirect.
|
|
||||||
template <bool obscured_indirectNoBase = true, bool has_base = true>
|
|
||||||
static uint32_t effective_address(
|
|
||||||
RegistersT ®isters,
|
|
||||||
const Instruction<is_32bit(model)> &instruction,
|
|
||||||
DataPointer pointer);
|
|
||||||
|
|
||||||
private:
|
|
||||||
template <bool is_write, typename DataT> static void access(
|
|
||||||
RegistersT ®isters,
|
|
||||||
MemoryT &memory,
|
|
||||||
const Instruction<is_32bit(model)> &instruction,
|
|
||||||
DataPointer pointer,
|
|
||||||
DataT &value);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Implementation begins here.
|
|
||||||
//
|
|
||||||
|
|
||||||
template <Model model, typename RegistersT, typename MemoryT>
|
|
||||||
template <typename DataT> DataT DataPointerResolver<model, RegistersT, MemoryT>::read(
|
|
||||||
RegistersT ®isters,
|
|
||||||
MemoryT &memory,
|
|
||||||
const Instruction<is_32bit(model)> &instruction,
|
|
||||||
DataPointer pointer) {
|
|
||||||
DataT result;
|
|
||||||
access<false>(registers, memory, instruction, pointer, result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <Model model, typename RegistersT, typename MemoryT>
|
|
||||||
template <typename DataT> void DataPointerResolver<model, RegistersT, MemoryT>::write(
|
|
||||||
RegistersT ®isters,
|
|
||||||
MemoryT &memory,
|
|
||||||
const Instruction<is_32bit(model)> &instruction,
|
|
||||||
DataPointer pointer,
|
|
||||||
DataT value) {
|
|
||||||
access<true>(registers, memory, instruction, pointer, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define rw(v, r, is_write) \
|
|
||||||
case Source::r: \
|
|
||||||
using VType = typename std::remove_reference<decltype(v)>::type; \
|
|
||||||
if constexpr (is_write) { \
|
|
||||||
registers.template write<VType, register_for_source<VType>(Source::r)>(v); \
|
|
||||||
} else { \
|
|
||||||
v = registers.template read<VType, register_for_source<VType>(Source::r)>(); \
|
|
||||||
} \
|
|
||||||
break;
|
|
||||||
|
|
||||||
#define ALLREGS(v, i) rw(v, eAX, i); rw(v, eCX, i); \
|
|
||||||
rw(v, eDX, i); rw(v, eBX, i); \
|
|
||||||
rw(v, eSPorAH, i); rw(v, eBPorCH, i); \
|
|
||||||
rw(v, eSIorDH, i); rw(v, eDIorBH, i); \
|
|
||||||
rw(v, ES, i); rw(v, CS, i); \
|
|
||||||
rw(v, SS, i); rw(v, DS, i); \
|
|
||||||
rw(v, FS, i); rw(v, GS, i);
|
|
||||||
|
|
||||||
template <Model model, typename RegistersT, typename MemoryT>
|
|
||||||
template <bool obscured_indirectNoBase, bool has_base>
|
|
||||||
uint32_t DataPointerResolver<model, RegistersT, MemoryT>::effective_address(
|
|
||||||
RegistersT ®isters,
|
|
||||||
const Instruction<is_32bit(model)> &instruction,
|
|
||||||
DataPointer pointer
|
|
||||||
) {
|
|
||||||
using AddressT = typename Instruction<is_32bit(model)>::AddressT;
|
|
||||||
AddressT base = 0, index = 0;
|
|
||||||
|
|
||||||
if constexpr (has_base) {
|
|
||||||
switch(pointer.base<obscured_indirectNoBase>()) {
|
|
||||||
default: break;
|
|
||||||
ALLREGS(base, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(pointer.index()) {
|
|
||||||
default: break;
|
|
||||||
ALLREGS(index, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t address = index;
|
|
||||||
if constexpr (model >= Model::i80386) {
|
|
||||||
address <<= pointer.scale();
|
|
||||||
} else {
|
|
||||||
assert(!pointer.scale());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always compute address as 32-bit.
|
|
||||||
// TODO: verify use of memory_mask around here.
|
|
||||||
// Also I think possibly an exception is supposed to be generated
|
|
||||||
// if the programmer is in 32-bit mode and has asked for 16-bit
|
|
||||||
// address computation but generated e.g. a 17-bit result. Look into
|
|
||||||
// that when working on execution. For now the goal is merely decoding
|
|
||||||
// and this code exists both to verify the presence of all necessary
|
|
||||||
// fields and to help to explore the best breakdown of storage
|
|
||||||
// within Instruction.
|
|
||||||
constexpr uint32_t memory_masks[] = {0x0000'ffff, 0xffff'ffff};
|
|
||||||
const uint32_t memory_mask = memory_masks[int(instruction.address_size())];
|
|
||||||
address = (address & memory_mask) + (base & memory_mask) + instruction.displacement();
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <Model model, typename RegistersT, typename MemoryT>
|
|
||||||
template <bool is_write, typename DataT> void DataPointerResolver<model, RegistersT, MemoryT>::access(
|
|
||||||
RegistersT ®isters,
|
|
||||||
MemoryT &memory,
|
|
||||||
const Instruction<is_32bit(model)> &instruction,
|
|
||||||
DataPointer pointer,
|
|
||||||
DataT &value) {
|
|
||||||
const Source source = pointer.source<false>();
|
|
||||||
|
|
||||||
switch(source) {
|
|
||||||
default:
|
|
||||||
if constexpr (!is_write) {
|
|
||||||
value = 0;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
|
|
||||||
ALLREGS(value, is_write);
|
|
||||||
|
|
||||||
case Source::DirectAddress:
|
|
||||||
if constexpr(is_write) {
|
|
||||||
memory.template write(instruction.segment_override(), instruction.displacement(), value);
|
|
||||||
} else {
|
|
||||||
value = memory.template read<DataT>(instruction.segment_override(), instruction.displacement());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Source::Immediate:
|
|
||||||
value = DataT(instruction.operand());
|
|
||||||
break;
|
|
||||||
|
|
||||||
// TODO: data_segment() will return Source::None if there was no override.
|
|
||||||
// Fix.
|
|
||||||
|
|
||||||
#define indirect(has_base) { \
|
|
||||||
const auto address = effective_address<false, has_base> \
|
|
||||||
(registers, instruction, pointer); \
|
|
||||||
\
|
|
||||||
if constexpr (is_write) { \
|
|
||||||
memory.template write( \
|
|
||||||
instruction.segment_override(), \
|
|
||||||
address, \
|
|
||||||
value \
|
|
||||||
); \
|
|
||||||
} else { \
|
|
||||||
value = memory.template read<DataT>( \
|
|
||||||
instruction.segment_override(), \
|
|
||||||
address \
|
|
||||||
); \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
case Source::IndirectNoBase:
|
|
||||||
indirect(false);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Source::Indirect:
|
|
||||||
indirect(true);
|
|
||||||
break;
|
|
||||||
#undef indirect
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#undef ALLREGS
|
|
||||||
#undef rw
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* DataPointerResolver_hpp */
|
|
@ -114,7 +114,7 @@ uint32_t address(
|
|||||||
RegistersT ®isters,
|
RegistersT ®isters,
|
||||||
MemoryT &memory
|
MemoryT &memory
|
||||||
) {
|
) {
|
||||||
switch(pointer.source<false>()) {
|
switch(pointer.source()) {
|
||||||
default: return 0;
|
default: return 0;
|
||||||
case Source::eAX: return *register_<model, IntT, Source::eAX>(registers);
|
case Source::eAX: return *register_<model, IntT, Source::eAX>(registers);
|
||||||
case Source::eCX: return *register_<model, IntT, Source::eCX>(registers);
|
case Source::eCX: return *register_<model, IntT, Source::eCX>(registers);
|
||||||
@ -844,7 +844,7 @@ void call_far(InstructionT &instruction,
|
|||||||
// 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();
|
||||||
switch(pointer.template source<false>()) {
|
switch(pointer.source()) {
|
||||||
default:
|
default:
|
||||||
case Source::Immediate: flow_controller.call(instruction.segment(), instruction.offset()); return;
|
case Source::Immediate: flow_controller.call(instruction.segment(), instruction.offset()); return;
|
||||||
|
|
||||||
@ -876,7 +876,7 @@ void jump_far(InstructionT &instruction,
|
|||||||
// 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();
|
||||||
switch(pointer.template source<false>()) {
|
switch(pointer.source()) {
|
||||||
default:
|
default:
|
||||||
case Source::Immediate: flow_controller.jump(instruction.segment(), instruction.offset()); return;
|
case Source::Immediate: flow_controller.jump(instruction.segment(), instruction.offset()); return;
|
||||||
|
|
||||||
@ -1547,7 +1547,7 @@ template <
|
|||||||
const auto source = [&]() -> IntT& {
|
const auto source = [&]() -> IntT& {
|
||||||
return *resolve<model, IntT>(
|
return *resolve<model, IntT>(
|
||||||
instruction,
|
instruction,
|
||||||
instruction.source().template source<false>(),
|
instruction.source().source(),
|
||||||
instruction.source(),
|
instruction.source(),
|
||||||
registers,
|
registers,
|
||||||
memory,
|
memory,
|
||||||
@ -1557,7 +1557,7 @@ template <
|
|||||||
const auto destination = [&]() -> IntT& {
|
const auto destination = [&]() -> IntT& {
|
||||||
return *resolve<model, IntT>(
|
return *resolve<model, IntT>(
|
||||||
instruction,
|
instruction,
|
||||||
instruction.destination().template source<false>(),
|
instruction.destination().source(),
|
||||||
instruction.destination(),
|
instruction.destination(),
|
||||||
registers,
|
registers,
|
||||||
memory,
|
memory,
|
||||||
@ -1576,7 +1576,7 @@ template <
|
|||||||
|
|
||||||
const auto shift_count = [&]() -> uint8_t {
|
const auto shift_count = [&]() -> uint8_t {
|
||||||
static constexpr uint8_t mask = (model != Model::i8086) ? 0x1f : 0xff;
|
static constexpr uint8_t mask = (model != Model::i8086) ? 0x1f : 0xff;
|
||||||
switch(instruction.source().template source<false>()) {
|
switch(instruction.source().source()) {
|
||||||
case Source::None: return 1;
|
case Source::None: return 1;
|
||||||
case Source::Immediate: return uint8_t(instruction.operand()) & mask;
|
case Source::Immediate: return uint8_t(instruction.operand()) & mask;
|
||||||
default: return registers.cl() & mask;
|
default: return registers.cl() & mask;
|
||||||
|
@ -372,7 +372,7 @@ std::string InstructionSet::x86::to_string(
|
|||||||
};
|
};
|
||||||
|
|
||||||
using Source = InstructionSet::x86::Source;
|
using Source = InstructionSet::x86::Source;
|
||||||
const Source source = pointer.source<false>();
|
const Source source = pointer.source();
|
||||||
switch(source) {
|
switch(source) {
|
||||||
// to_string handles all direct register names correctly.
|
// to_string handles all direct register names correctly.
|
||||||
default: return InstructionSet::x86::to_string(source, operation_size);
|
default: return InstructionSet::x86::to_string(source, operation_size);
|
||||||
|
@ -595,10 +595,7 @@ class DataPointer {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool obscure_indirectNoBase = false> constexpr Source source() const {
|
constexpr Source source() const {
|
||||||
if constexpr (obscure_indirectNoBase) {
|
|
||||||
return (source_ >= Source::IndirectNoBase) ? Source::Indirect : source_;
|
|
||||||
}
|
|
||||||
return source_;
|
return source_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -634,10 +631,7 @@ class DataPointer {
|
|||||||
return Source::DS;
|
return Source::DS;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool obscure_indirectNoBase = false> constexpr Source base() const {
|
constexpr Source base() const {
|
||||||
if constexpr (obscure_indirectNoBase) {
|
|
||||||
return (source_ <= Source::IndirectNoBase) ? Source::None : sib_.base();
|
|
||||||
}
|
|
||||||
return sib_.base();
|
return sib_.base();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -805,11 +799,11 @@ template<bool is_32bit> class Instruction {
|
|||||||
return DataSize(source_data_dest_sib_ >> 14);
|
return DataSize(source_data_dest_sib_ >> 14);
|
||||||
}
|
}
|
||||||
|
|
||||||
int length() const {
|
// int length() const {
|
||||||
const int short_length = (source_data_dest_sib_ >> 10) & 15;
|
// const int short_length = (source_data_dest_sib_ >> 10) & 15;
|
||||||
if(short_length) return short_length;
|
// if(short_length) return short_length;
|
||||||
return length_extension() >> 6;
|
// return length_extension() >> 6;
|
||||||
}
|
// }
|
||||||
|
|
||||||
ImmediateT operand() const {
|
ImmediateT operand() const {
|
||||||
const ImmediateT ops[] = {0, operand_extension()};
|
const ImmediateT ops[] = {0, operand_extension()};
|
||||||
|
@ -1048,7 +1048,6 @@
|
|||||||
4BE21219253FCE9C00435408 /* AppleIIgs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE21214253FCE9C00435408 /* AppleIIgs.cpp */; };
|
4BE21219253FCE9C00435408 /* AppleIIgs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE21214253FCE9C00435408 /* AppleIIgs.cpp */; };
|
||||||
4BE2121A253FCE9C00435408 /* AppleIIgs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE21214253FCE9C00435408 /* AppleIIgs.cpp */; };
|
4BE2121A253FCE9C00435408 /* AppleIIgs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE21214253FCE9C00435408 /* AppleIIgs.cpp */; };
|
||||||
4BE34438238389E10058E78F /* AtariSTVideoTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE34437238389E10058E78F /* AtariSTVideoTests.mm */; };
|
4BE34438238389E10058E78F /* AtariSTVideoTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE34437238389E10058E78F /* AtariSTVideoTests.mm */; };
|
||||||
4BE3C69727CC32DC000EAD28 /* x86DataPointerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE3C69627CC32DC000EAD28 /* x86DataPointerTests.mm */; };
|
|
||||||
4BE76CF922641ED400ACD6FA /* QLTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE76CF822641ED300ACD6FA /* QLTests.mm */; };
|
4BE76CF922641ED400ACD6FA /* QLTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE76CF822641ED300ACD6FA /* QLTests.mm */; };
|
||||||
4BE8EB6625C750B50040BC40 /* DAT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE8EB6425C750B50040BC40 /* DAT.cpp */; };
|
4BE8EB6625C750B50040BC40 /* DAT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE8EB6425C750B50040BC40 /* DAT.cpp */; };
|
||||||
4BE90FFD22D5864800FB464D /* MacintoshVideoTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */; };
|
4BE90FFD22D5864800FB464D /* MacintoshVideoTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */; };
|
||||||
@ -2214,9 +2213,7 @@
|
|||||||
4BE3231520532AA7006EF799 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
|
4BE3231520532AA7006EF799 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
|
||||||
4BE3231620532BED006EF799 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
|
4BE3231620532BED006EF799 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
|
||||||
4BE34437238389E10058E78F /* AtariSTVideoTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AtariSTVideoTests.mm; sourceTree = "<group>"; };
|
4BE34437238389E10058E78F /* AtariSTVideoTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AtariSTVideoTests.mm; sourceTree = "<group>"; };
|
||||||
4BE3C69327C793EF000EAD28 /* DataPointerResolver.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DataPointerResolver.hpp; sourceTree = "<group>"; };
|
|
||||||
4BE3C69527CBC540000EAD28 /* Model.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Model.hpp; sourceTree = "<group>"; };
|
4BE3C69527CBC540000EAD28 /* Model.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Model.hpp; sourceTree = "<group>"; };
|
||||||
4BE3C69627CC32DC000EAD28 /* x86DataPointerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = x86DataPointerTests.mm; sourceTree = "<group>"; };
|
|
||||||
4BE76CF822641ED300ACD6FA /* QLTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = QLTests.mm; sourceTree = "<group>"; };
|
4BE76CF822641ED300ACD6FA /* QLTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = QLTests.mm; sourceTree = "<group>"; };
|
||||||
4BE845201F2FF7F100A5EA22 /* CRTC6845.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRTC6845.hpp; sourceTree = "<group>"; };
|
4BE845201F2FF7F100A5EA22 /* CRTC6845.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRTC6845.hpp; sourceTree = "<group>"; };
|
||||||
4BE8EB5425C0E9D40040BC40 /* Disassembler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Disassembler.hpp; sourceTree = "<group>"; };
|
4BE8EB5425C0E9D40040BC40 /* Disassembler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Disassembler.hpp; sourceTree = "<group>"; };
|
||||||
@ -4393,7 +4390,6 @@
|
|||||||
4B8DD3672633B2D400B3C866 /* SpectrumVideoContentionTests.mm */,
|
4B8DD3672633B2D400B3C866 /* SpectrumVideoContentionTests.mm */,
|
||||||
4B2AF8681E513FC20027EE29 /* TIATests.mm */,
|
4B2AF8681E513FC20027EE29 /* TIATests.mm */,
|
||||||
4B1D08051E0F7A1100763741 /* TimeTests.mm */,
|
4B1D08051E0F7A1100763741 /* TimeTests.mm */,
|
||||||
4BE3C69627CC32DC000EAD28 /* x86DataPointerTests.mm */,
|
|
||||||
4BEE4BD325A26E2B00011BD2 /* x86DecoderTests.mm */,
|
4BEE4BD325A26E2B00011BD2 /* x86DecoderTests.mm */,
|
||||||
4BDA8234261E8E000021AA19 /* Z80ContentionTests.mm */,
|
4BDA8234261E8E000021AA19 /* Z80ContentionTests.mm */,
|
||||||
4BB73EB81B587A5100552FC2 /* Info.plist */,
|
4BB73EB81B587A5100552FC2 /* Info.plist */,
|
||||||
@ -5001,7 +4997,6 @@
|
|||||||
children = (
|
children = (
|
||||||
4BEDA3B925B25563000C2DBD /* Decoder.cpp */,
|
4BEDA3B925B25563000C2DBD /* Decoder.cpp */,
|
||||||
4B69DEB52AB79E4F0055B217 /* Instruction.cpp */,
|
4B69DEB52AB79E4F0055B217 /* Instruction.cpp */,
|
||||||
4BE3C69327C793EF000EAD28 /* DataPointerResolver.hpp */,
|
|
||||||
4BEDA3B825B25563000C2DBD /* Decoder.hpp */,
|
4BEDA3B825B25563000C2DBD /* Decoder.hpp */,
|
||||||
4BEDA3DB25B2588F000C2DBD /* Instruction.hpp */,
|
4BEDA3DB25B2588F000C2DBD /* Instruction.hpp */,
|
||||||
4BE3C69527CBC540000EAD28 /* Model.hpp */,
|
4BE3C69527CBC540000EAD28 /* Model.hpp */,
|
||||||
@ -6251,7 +6246,6 @@
|
|||||||
4B7752AA28217E370073E2C5 /* ROMCatalogue.cpp in Sources */,
|
4B7752AA28217E370073E2C5 /* ROMCatalogue.cpp in Sources */,
|
||||||
4B778F0323A5EBB00000D260 /* FAT12.cpp in Sources */,
|
4B778F0323A5EBB00000D260 /* FAT12.cpp in Sources */,
|
||||||
4B778F4023A5F1910000D260 /* z8530.cpp in Sources */,
|
4B778F4023A5F1910000D260 /* z8530.cpp in Sources */,
|
||||||
4BE3C69727CC32DC000EAD28 /* x86DataPointerTests.mm in Sources */,
|
|
||||||
4B778EFD23A5EB8E0000D260 /* AppleDSK.cpp in Sources */,
|
4B778EFD23A5EB8E0000D260 /* AppleDSK.cpp in Sources */,
|
||||||
4B7752B728217EF40073E2C5 /* Chipset.cpp in Sources */,
|
4B7752B728217EF40073E2C5 /* Chipset.cpp in Sources */,
|
||||||
4B778EFB23A5EB7E0000D260 /* HFE.cpp in Sources */,
|
4B778EFB23A5EB7E0000D260 /* HFE.cpp in Sources */,
|
||||||
|
@ -1,103 +0,0 @@
|
|||||||
//
|
|
||||||
// x86DataPointerTests.m
|
|
||||||
// Clock Signal
|
|
||||||
//
|
|
||||||
// Created by Thomas Harte on 27/02/2022.
|
|
||||||
// Copyright 2022 Thomas Harte. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <XCTest/XCTest.h>
|
|
||||||
|
|
||||||
#include "../../../InstructionSets/x86/DataPointerResolver.hpp"
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
using namespace InstructionSet::x86;
|
|
||||||
|
|
||||||
@interface x86DataPointerTests : XCTestCase
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation x86DataPointerTests
|
|
||||||
|
|
||||||
- (void)test16bitSize1 {
|
|
||||||
const DataPointer indirectPointer(
|
|
||||||
Source::eAX, Source::eDI, 0
|
|
||||||
);
|
|
||||||
const DataPointer registerPointer(
|
|
||||||
Source::eBX
|
|
||||||
);
|
|
||||||
|
|
||||||
struct Registers {
|
|
||||||
uint16_t ax = 0x1234, di = 0x00ee;
|
|
||||||
uint8_t bl = 0xaa;
|
|
||||||
|
|
||||||
template <typename DataT, Register r> DataT read() {
|
|
||||||
assert(is_sized<DataT>(r));
|
|
||||||
switch(r) {
|
|
||||||
case Register::AX: return ax;
|
|
||||||
case Register::BL: return bl;
|
|
||||||
case Register::DI: return di;
|
|
||||||
default: return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template <typename DataT, Register r> void write(DataT value) {
|
|
||||||
assert(is_sized<DataT>(r));
|
|
||||||
switch(r) {
|
|
||||||
case Register::BL: bl = value; break;
|
|
||||||
default: assert(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} registers;
|
|
||||||
|
|
||||||
struct Memory {
|
|
||||||
std::map<uint32_t, uint8_t> data;
|
|
||||||
|
|
||||||
template<typename DataT> DataT read(Source, uint32_t address) {
|
|
||||||
if(address == 0x1234 + 0x00ee) return 0xff;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
template<typename DataT> void write(Source, uint32_t address, DataT value) {
|
|
||||||
data[address] = value;
|
|
||||||
}
|
|
||||||
} memory;
|
|
||||||
|
|
||||||
// TODO: construct this more formally; the code below just assumes size = 1, which is not a contractual guarantee.
|
|
||||||
const auto instruction = Instruction<false>();
|
|
||||||
|
|
||||||
using Resolver = DataPointerResolver<Model::i8086, Registers, Memory>;
|
|
||||||
const uint8_t memoryValue = Resolver::read<uint8_t>(
|
|
||||||
registers,
|
|
||||||
memory,
|
|
||||||
instruction,
|
|
||||||
indirectPointer
|
|
||||||
);
|
|
||||||
registers.ax = 0x0100;
|
|
||||||
Resolver::write<uint8_t>(
|
|
||||||
registers,
|
|
||||||
memory,
|
|
||||||
instruction,
|
|
||||||
indirectPointer,
|
|
||||||
0xef
|
|
||||||
);
|
|
||||||
|
|
||||||
XCTAssertEqual(memoryValue, 0xff);
|
|
||||||
XCTAssertEqual(memory.data[0x01ee], 0xef);
|
|
||||||
|
|
||||||
const uint8_t registerValue = Resolver::read<uint8_t>(
|
|
||||||
registers,
|
|
||||||
memory,
|
|
||||||
instruction,
|
|
||||||
registerPointer
|
|
||||||
);
|
|
||||||
Resolver::write<uint8_t>(
|
|
||||||
registers,
|
|
||||||
memory,
|
|
||||||
instruction,
|
|
||||||
registerPointer,
|
|
||||||
0x93
|
|
||||||
);
|
|
||||||
|
|
||||||
XCTAssertEqual(registerValue, 0xaa);
|
|
||||||
XCTAssertEqual(registers.bl, 0x93);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
@ -12,7 +12,6 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "../../../InstructionSets/x86/Decoder.hpp"
|
#include "../../../InstructionSets/x86/Decoder.hpp"
|
||||||
#include "../../../InstructionSets/x86/DataPointerResolver.hpp"
|
|
||||||
|
|
||||||
using namespace InstructionSet::x86;
|
using namespace InstructionSet::x86;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user