1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-26 23:52:26 +00:00

Propagate access types, even if incorrect.

This commit is contained in:
Thomas Harte 2023-10-29 14:33:39 -04:00
parent 8153cec455
commit 7a886f938a
2 changed files with 72 additions and 68 deletions

View File

@ -18,7 +18,7 @@
namespace InstructionSet::x86 {
template <Model model, typename IntT, typename InstructionT, typename RegistersT, typename MemoryT>
template <Model model, typename IntT, AccessType access, typename InstructionT, typename RegistersT, typename MemoryT>
IntT *resolve(
InstructionT &instruction,
Source source,
@ -29,7 +29,7 @@ IntT *resolve(
IntT *immediate = nullptr
);
template <Model model, Source source, typename IntT, typename InstructionT, typename RegistersT, typename MemoryT>
template <Model model, Source source, typename IntT, AccessType access, typename InstructionT, typename RegistersT, typename MemoryT>
uint32_t address(
InstructionT &instruction,
DataPointer pointer,
@ -43,7 +43,7 @@ uint32_t address(
uint32_t address;
uint16_t zero = 0;
address = *resolve<model, uint16_t>(instruction, pointer.index(), pointer, registers, memory, &zero);
address = *resolve<model, uint16_t, access>(instruction, pointer.index(), pointer, registers, memory, &zero);
if constexpr (is_32bit(model)) {
address <<= pointer.scale();
}
@ -52,10 +52,10 @@ uint32_t address(
if constexpr (source == Source::IndirectNoBase) {
return address;
}
return address + *resolve<model, uint16_t>(instruction, pointer.base(), pointer, registers, memory);
return address + *resolve<model, uint16_t, access>(instruction, pointer.base(), pointer, registers, memory);
}
template <Model model, typename IntT, Source source, typename RegistersT>
template <Model model, typename IntT, AccessType access, Source source, typename RegistersT>
IntT *register_(RegistersT &registers) {
switch(source) {
case Source::eAX:
@ -107,30 +107,32 @@ IntT *register_(RegistersT &registers) {
}
}
template <Model model, typename IntT, typename InstructionT, typename RegistersT, typename MemoryT>
template <Model model, typename IntT, AccessType access, typename InstructionT, typename RegistersT, typename MemoryT>
uint32_t address(
InstructionT &instruction,
DataPointer pointer,
RegistersT &registers,
MemoryT &memory
) {
// 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_<model, IntT, Source::eAX>(registers);
case Source::eCX: return *register_<model, IntT, Source::eCX>(registers);
case Source::eDX: return *register_<model, IntT, Source::eDX>(registers);
case Source::eBX: return *register_<model, IntT, Source::eBX>(registers);
case Source::eSPorAH: return *register_<model, IntT, Source::eSPorAH>(registers);
case Source::eBPorCH: return *register_<model, IntT, Source::eBPorCH>(registers);
case Source::eSIorDH: return *register_<model, IntT, Source::eSIorDH>(registers);
case Source::eDIorBH: return *register_<model, IntT, Source::eDIorBH>(registers);
case Source::Indirect: return address<model, Source::Indirect, IntT>(instruction, pointer, registers, memory);
case Source::IndirectNoBase: return address<model, Source::IndirectNoBase, IntT>(instruction, pointer, registers, memory);
case Source::DirectAddress: return address<model, Source::DirectAddress, IntT>(instruction, pointer, registers, memory);
case Source::eAX: return *register_<model, IntT, access, Source::eAX>(registers);
case Source::eCX: return *register_<model, IntT, access, Source::eCX>(registers);
case Source::eDX: return *register_<model, IntT, access, Source::eDX>(registers);
case Source::eBX: return *register_<model, IntT, access, Source::eBX>(registers);
case Source::eSPorAH: return *register_<model, IntT, access, Source::eSPorAH>(registers);
case Source::eBPorCH: return *register_<model, IntT, access, Source::eBPorCH>(registers);
case Source::eSIorDH: return *register_<model, IntT, access, Source::eSIorDH>(registers);
case Source::eDIorBH: return *register_<model, IntT, access, Source::eDIorBH>(registers);
case Source::Indirect: return address<model, Source::Indirect, IntT, access>(instruction, pointer, registers, memory);
case Source::IndirectNoBase: return address<model, Source::IndirectNoBase, IntT, access>(instruction, pointer, registers, memory);
case Source::DirectAddress: return address<model, Source::DirectAddress, IntT, access>(instruction, pointer, registers, memory);
}
}
template <Model model, typename IntT, typename InstructionT, typename RegistersT, typename MemoryT>
template <Model model, typename IntT, AccessType access, typename InstructionT, typename RegistersT, typename MemoryT>
IntT *resolve(
InstructionT &instruction,
Source source,
@ -146,14 +148,14 @@ IntT *resolve(
// * otherwise return the appropriate value.
uint32_t target_address;
switch(source) {
case Source::eAX: return register_<model, IntT, Source::eAX>(registers);
case Source::eCX: return register_<model, IntT, Source::eCX>(registers);
case Source::eDX: return register_<model, IntT, Source::eDX>(registers);
case Source::eBX: return register_<model, IntT, Source::eBX>(registers);
case Source::eSPorAH: return register_<model, IntT, Source::eSPorAH>(registers);
case Source::eBPorCH: return register_<model, IntT, Source::eBPorCH>(registers);
case Source::eSIorDH: return register_<model, IntT, Source::eSIorDH>(registers);
case Source::eDIorBH: return register_<model, IntT, Source::eDIorBH>(registers);
case Source::eAX: return register_<model, IntT, access, Source::eAX>(registers);
case Source::eCX: return register_<model, IntT, access, Source::eCX>(registers);
case Source::eDX: return register_<model, IntT, access, Source::eDX>(registers);
case Source::eBX: return register_<model, IntT, access, Source::eBX>(registers);
case Source::eSPorAH: return register_<model, IntT, access, Source::eSPorAH>(registers);
case Source::eBPorCH: return register_<model, IntT, access, Source::eBPorCH>(registers);
case Source::eSIorDH: return register_<model, IntT, access, Source::eSIorDH>(registers);
case Source::eDIorBH: return register_<model, IntT, access, Source::eDIorBH>(registers);
// Segment registers are always 16-bit.
case Source::ES: if constexpr (std::is_same_v<IntT, uint16_t>) return &registers.es(); else return nullptr;
@ -172,19 +174,19 @@ IntT *resolve(
case Source::None: return none;
case Source::Indirect:
target_address = address<model, Source::Indirect, IntT>(instruction, pointer, registers, memory);
target_address = address<model, Source::Indirect, IntT, access>(instruction, pointer, registers, memory);
break;
case Source::IndirectNoBase:
target_address = address<model, Source::IndirectNoBase, IntT>(instruction, pointer, registers, memory);
target_address = address<model, Source::IndirectNoBase, IntT, access>(instruction, pointer, registers, memory);
break;
case Source::DirectAddress:
target_address = address<model, Source::DirectAddress, IntT>(instruction, pointer, registers, memory);
target_address = address<model, Source::DirectAddress, IntT, access>(instruction, pointer, registers, memory);
break;
}
// If execution has reached here then a memory fetch is required.
// Do it and exit.
return &memory.template access<IntT>(instruction.data_segment(), target_address);
return &memory.template access<IntT, access>(instruction.data_segment(), target_address);
};
namespace Primitive {
@ -194,7 +196,7 @@ namespace Primitive {
template <typename IntT, typename MemoryT, typename RegistersT>
void push(IntT &value, MemoryT &memory, RegistersT &registers) {
registers.sp_ -= sizeof(IntT);
memory.template access<IntT>(
memory.template access<IntT, AccessType::Write>(
InstructionSet::x86::Source::SS,
registers.sp_) = value;
memory.template write_back<IntT>();
@ -202,7 +204,7 @@ void push(IntT &value, MemoryT &memory, RegistersT &registers) {
template <typename IntT, typename MemoryT, typename RegistersT>
IntT pop(MemoryT &memory, RegistersT &registers) {
const auto value = memory.template access<IntT>(
const auto value = memory.template access<IntT, AccessType::Write>(
InstructionSet::x86::Source::SS,
registers.sp_);
registers.sp_ += sizeof(IntT);
@ -848,21 +850,21 @@ void call_far(InstructionT &instruction,
case Source::Immediate: flow_controller.call(instruction.segment(), instruction.offset()); return;
case Source::Indirect:
source_address = address<model, Source::Indirect, uint16_t>(instruction, pointer, registers, memory);
source_address = address<model, Source::Indirect, uint16_t, AccessType::PreAuthorised>(instruction, pointer, registers, memory);
break;
case Source::IndirectNoBase:
source_address = address<model, Source::IndirectNoBase, uint16_t>(instruction, pointer, registers, memory);
source_address = address<model, Source::IndirectNoBase, uint16_t, AccessType::PreAuthorised>(instruction, pointer, registers, memory);
break;
case Source::DirectAddress:
source_address = address<model, Source::DirectAddress, uint16_t>(instruction, pointer, registers, memory);
source_address = address<model, Source::DirectAddress, uint16_t, AccessType::PreAuthorised>(instruction, pointer, registers, memory);
break;
}
const Source source_segment = instruction.data_segment();
const uint16_t offset = memory.template access<uint16_t>(source_segment, source_address);
const uint16_t offset = memory.template access<uint16_t, AccessType::Read>(source_segment, source_address);
source_address += 2;
const uint16_t segment = memory.template access<uint16_t>(source_segment, source_address);
const uint16_t segment = memory.template access<uint16_t, AccessType::Read>(source_segment, source_address);
flow_controller.call(segment, offset);
}
@ -880,21 +882,21 @@ void jump_far(InstructionT &instruction,
case Source::Immediate: flow_controller.jump(instruction.segment(), instruction.offset()); return;
case Source::Indirect:
source_address = address<model, Source::Indirect, uint16_t>(instruction, pointer, registers, memory);
source_address = address<model, Source::Indirect, uint16_t, AccessType::Read>(instruction, pointer, registers, memory);
break;
case Source::IndirectNoBase:
source_address = address<model, Source::IndirectNoBase, uint16_t>(instruction, pointer, registers, memory);
source_address = address<model, Source::IndirectNoBase, uint16_t, AccessType::Read>(instruction, pointer, registers, memory);
break;
case Source::DirectAddress:
source_address = address<model, Source::DirectAddress, uint16_t>(instruction, pointer, registers, memory);
source_address = address<model, Source::DirectAddress, uint16_t, AccessType::Read>(instruction, pointer, registers, memory);
break;
}
const Source source_segment = instruction.data_segment();
const uint16_t offset = memory.template access<uint16_t>(source_segment, source_address);
const uint16_t offset = memory.template access<uint16_t, AccessType::Read>(source_segment, source_address);
source_address += 2;
const uint16_t segment = memory.template access<uint16_t>(source_segment, source_address);
const uint16_t segment = memory.template access<uint16_t, AccessType::Read>(source_segment, source_address);
flow_controller.jump(segment, offset);
}
@ -930,14 +932,14 @@ void ld(
RegistersT &registers
) {
const auto pointer = instruction.source();
auto source_address = address<model, uint16_t>(instruction, pointer, registers, memory);
auto source_address = address<model, uint16_t, AccessType::Read>(instruction, pointer, registers, memory);
const Source source_segment = instruction.data_segment();
destination = memory.template access<uint16_t>(source_segment, source_address);
destination = memory.template access<uint16_t, AccessType::Read>(source_segment, source_address);
source_address += 2;
switch(selector) {
case Source::DS: registers.ds() = memory.template access<uint16_t>(source_segment, source_address); break;
case Source::ES: registers.es() = memory.template access<uint16_t>(source_segment, source_address); break;
case Source::DS: registers.ds() = memory.template access<uint16_t, AccessType::Read>(source_segment, source_address); break;
case Source::ES: registers.es() = memory.template access<uint16_t, AccessType::Read>(source_segment, source_address); break;
}
}
@ -949,7 +951,7 @@ void lea(
RegistersT &registers
) {
// TODO: address size.
destination = IntT(address<model, uint16_t>(instruction, instruction.source(), registers, memory));
destination = IntT(address<model, uint16_t, AccessType::PreAuthorised>(instruction, instruction.source(), registers, memory));
}
template <typename AddressT, typename InstructionT, typename MemoryT, typename RegistersT>
@ -963,7 +965,7 @@ void xlat(
address = registers.bx() + registers.al();
}
registers.al() = memory.template access<uint8_t>(instruction.data_segment(), address);
registers.al() = memory.template access<uint8_t, AccessType::Read>(instruction.data_segment(), address);
}
template <typename IntT>
@ -1403,8 +1405,8 @@ void cmps(const InstructionT &instruction, AddressT &eCX, AddressT &eSI, Address
return;
}
IntT lhs = memory.template access<IntT>(instruction.data_segment(), eSI);
const IntT rhs = memory.template access<IntT>(Source::ES, eDI);
IntT lhs = memory.template access<IntT, AccessType::Read>(instruction.data_segment(), eSI);
const IntT rhs = memory.template access<IntT, AccessType::Write>(Source::ES, eDI);
eSI += status.direction<AddressT>() * sizeof(IntT);
eDI += status.direction<AddressT>() * sizeof(IntT);
@ -1419,7 +1421,7 @@ void scas(AddressT &eCX, AddressT &eDI, IntT &eAX, MemoryT &memory, Status &stat
return;
}
const IntT rhs = memory.template access<IntT>(Source::ES, eDI);
const IntT rhs = memory.template access<IntT, AccessType::Read>(Source::ES, eDI);
eDI += status.direction<AddressT>() * sizeof(IntT);
Primitive::sub<false, false>(eAX, rhs, status);
@ -1433,7 +1435,7 @@ void lods(const InstructionT &instruction, AddressT &eCX, AddressT &eSI, IntT &e
return;
}
eAX = memory.template access<IntT>(instruction.data_segment(), eSI);
eAX = memory.template access<IntT, AccessType::Read>(instruction.data_segment(), eSI);
eSI += status.direction<AddressT>() * sizeof(IntT);
repeat<AddressT, repetition>(status, eCX, flow_controller);
@ -1445,7 +1447,7 @@ void movs(const InstructionT &instruction, AddressT &eCX, AddressT &eSI, Address
return;
}
memory.template access<IntT>(Source::ES, eDI) = memory.template access<IntT>(instruction.data_segment(), eSI);
memory.template access<IntT, AccessType::Write>(Source::ES, eDI) = memory.template access<IntT, AccessType::Read>(instruction.data_segment(), eSI);
eSI += status.direction<AddressT>() * sizeof(IntT);
eDI += status.direction<AddressT>() * sizeof(IntT);
@ -1459,7 +1461,7 @@ void stos(AddressT &eCX, AddressT &eDI, IntT &eAX, MemoryT &memory, Status &stat
return;
}
memory.template access<IntT>(Source::ES, eDI) = eAX;
memory.template access<IntT, AccessType::Write>(Source::ES, eDI) = eAX;
eDI += status.direction<AddressT>() * sizeof(IntT);
repeat<AddressT, repetition>(status, eCX, flow_controller);
@ -1471,7 +1473,7 @@ void outs(const InstructionT &instruction, AddressT &eCX, uint16_t port, Address
return;
}
io.template out<IntT>(port, memory.template access<IntT>(instruction.data_segment(), eSI));
io.template out<IntT>(port, memory.template access<IntT, AccessType::Read>(instruction.data_segment(), eSI));
eSI += status.direction<AddressT>() * sizeof(IntT);
repeat<AddressT, repetition>(status, eCX, flow_controller);
@ -1483,7 +1485,7 @@ void ins(AddressT &eCX, uint16_t port, AddressT &eDI, MemoryT &memory, IOT &io,
return;
}
memory.template access<IntT>(Source::ES, eDI) = io.template in<IntT>(port);
memory.template access<IntT, AccessType::Write>(Source::ES, eDI) = io.template in<IntT>(port);
eDI += status.direction<AddressT>() * sizeof(IntT);
repeat<AddressT, repetition>(status, eCX, flow_controller);
@ -1524,7 +1526,7 @@ template <
// Establish source() and destination() shorthand to fetch data if necessary.
IntT immediate;
const auto source = [&]() -> IntT& {
return *resolve<model, IntT>(
return *resolve<model, IntT, AccessType::PreAuthorised>(
instruction,
instruction.source().source(),
instruction.source(),
@ -1534,7 +1536,7 @@ template <
&immediate);
};
const auto destination = [&]() -> IntT& {
return *resolve<model, IntT>(
return *resolve<model, IntT, AccessType::PreAuthorised>(
instruction,
instruction.destination().source(),
instruction.destination(),

View File

@ -25,7 +25,7 @@ namespace {
// The tests themselves are not duplicated in this repository;
// provide their real path here.
constexpr char TestSuiteHome[] = "/Users/tharte/Projects/ProcessorTests/8088/v1";
constexpr char TestSuiteHome[] = "/Users/thomasharte/Projects/ProcessorTests/8088/v1";
using Status = InstructionSet::x86::Status;
struct Registers {
@ -91,6 +91,7 @@ struct Registers {
}
};
struct Memory {
using AccessType = InstructionSet::x86::AccessType;
enum class Tag {
Seeded,
AccessExpected,
@ -134,14 +135,14 @@ struct Memory {
// Entry point used by the flow controller so that it can mark up locations at which the flags were written,
// so that defined-flag-only masks can be applied while verifying RAM contents.
template <typename IntT> IntT &access(InstructionSet::x86::Source segment, uint16_t address, Tag tag) {
template <typename IntT, AccessType type> IntT &access(InstructionSet::x86::Source segment, uint16_t address, Tag tag) {
const uint32_t physical_address = (segment_base(segment) + address) & 0xf'ffff;
return access<IntT>(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
// to a selector, they're just at an absolute location.
template <typename IntT> IntT &access(uint32_t address, Tag tag) {
template <typename IntT, AccessType type> IntT &access(uint32_t address, Tag tag) {
// Check for address wraparound
if(address >= 0x10'0001 - sizeof(IntT)) {
if constexpr (std::is_same_v<IntT, uint8_t>) {
@ -167,7 +168,7 @@ struct Memory {
}
// Entry point for the 8086; simply notes that memory was accessed.
template <typename IntT> IntT &access([[maybe_unused]] InstructionSet::x86::Source segment, uint32_t address) {
template <typename IntT, AccessType type> IntT &access([[maybe_unused]] InstructionSet::x86::Source segment, uint32_t address) {
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.
@ -178,7 +179,7 @@ struct Memory {
return write_back_value_;
}
}
return access<IntT>(segment, address, Tag::Accessed);
return access<IntT, type>(segment, address, Tag::Accessed);
}
template <typename IntT>
@ -210,9 +211,10 @@ class FlowController {
void did_far_ret() {}
void interrupt(int index) {
// TODO: reauthorise and possibly double fault?
const uint16_t address = static_cast<uint16_t>(index) << 2;
const uint16_t new_ip = memory_.access<uint16_t>(address, Memory::Tag::Accessed);
const uint16_t new_cs = memory_.access<uint16_t>(address + 2, Memory::Tag::Accessed);
const uint16_t new_ip = memory_.access<uint16_t, Memory::AccessType::Read>(address, Memory::Tag::Accessed);
const uint16_t new_cs = memory_.access<uint16_t, Memory::AccessType::Read>(address + 2, Memory::Tag::Accessed);
push(status_.get(), true);
@ -270,13 +272,13 @@ class FlowController {
// Perform the push in two steps because it's possible for SP to underflow, and so that FlagsL and
// FlagsH can be set separately.
--registers_.sp_;
memory_.access<uint8_t>(
memory_.access<uint8_t, Memory::AccessType::Write>(
InstructionSet::x86::Source::SS,
registers_.sp_,
is_flags ? Memory::Tag::FlagsH : Memory::Tag::Accessed
) = value >> 8;
--registers_.sp_;
memory_.access<uint8_t>(
memory_.access<uint8_t, Memory::AccessType::Write>(
InstructionSet::x86::Source::SS,
registers_.sp_,
is_flags ? Memory::Tag::FlagsL : Memory::Tag::Accessed