diff --git a/InstructionSets/x86/Implementation/PerformImplementation.hpp b/InstructionSets/x86/Implementation/PerformImplementation.hpp index 5d840289e..ae1458497 100644 --- a/InstructionSets/x86/Implementation/PerformImplementation.hpp +++ b/InstructionSets/x86/Implementation/PerformImplementation.hpp @@ -18,7 +18,7 @@ namespace InstructionSet::x86 { -template +template IntT *resolve( InstructionT &instruction, Source source, @@ -29,7 +29,7 @@ IntT *resolve( IntT *immediate = nullptr ); -template +template uint32_t address( InstructionT &instruction, DataPointer pointer, @@ -43,7 +43,7 @@ uint32_t address( uint32_t address; uint16_t zero = 0; - address = *resolve(instruction, pointer.index(), pointer, registers, memory, &zero); + address = *resolve(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(instruction, pointer.base(), pointer, registers, memory); + return address + *resolve(instruction, pointer.base(), pointer, registers, memory); } -template +template IntT *register_(RegistersT ®isters) { switch(source) { case Source::eAX: @@ -107,30 +107,32 @@ IntT *register_(RegistersT ®isters) { } } -template +template uint32_t address( InstructionT &instruction, DataPointer pointer, RegistersT ®isters, 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_(registers); - case Source::eCX: return *register_(registers); - case Source::eDX: return *register_(registers); - case Source::eBX: return *register_(registers); - case Source::eSPorAH: return *register_(registers); - case Source::eBPorCH: return *register_(registers); - case Source::eSIorDH: return *register_(registers); - case Source::eDIorBH: return *register_(registers); - case Source::Indirect: return address(instruction, pointer, registers, memory); - case Source::IndirectNoBase: return address(instruction, pointer, registers, memory); - case Source::DirectAddress: return address(instruction, pointer, registers, memory); + case Source::eAX: return *register_(registers); + case Source::eCX: return *register_(registers); + case Source::eDX: return *register_(registers); + case Source::eBX: return *register_(registers); + case Source::eSPorAH: return *register_(registers); + case Source::eBPorCH: return *register_(registers); + case Source::eSIorDH: return *register_(registers); + case Source::eDIorBH: return *register_(registers); + case Source::Indirect: return address(instruction, pointer, registers, memory); + case Source::IndirectNoBase: return address(instruction, pointer, registers, memory); + case Source::DirectAddress: return address(instruction, pointer, registers, memory); } } -template +template 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_(registers); - case Source::eCX: return register_(registers); - case Source::eDX: return register_(registers); - case Source::eBX: return register_(registers); - case Source::eSPorAH: return register_(registers); - case Source::eBPorCH: return register_(registers); - case Source::eSIorDH: return register_(registers); - case Source::eDIorBH: return register_(registers); + case Source::eAX: return register_(registers); + case Source::eCX: return register_(registers); + case Source::eDX: return register_(registers); + case Source::eBX: return register_(registers); + case Source::eSPorAH: return register_(registers); + case Source::eBPorCH: return register_(registers); + case Source::eSIorDH: return register_(registers); + case Source::eDIorBH: return register_(registers); // Segment registers are always 16-bit. case Source::ES: if constexpr (std::is_same_v) return ®isters.es(); else return nullptr; @@ -172,19 +174,19 @@ IntT *resolve( case Source::None: return none; case Source::Indirect: - target_address = address(instruction, pointer, registers, memory); + target_address = address(instruction, pointer, registers, memory); break; case Source::IndirectNoBase: - target_address = address(instruction, pointer, registers, memory); + target_address = address(instruction, pointer, registers, memory); break; case Source::DirectAddress: - target_address = address(instruction, pointer, registers, memory); + target_address = address(instruction, pointer, registers, memory); break; } // If execution has reached here then a memory fetch is required. // Do it and exit. - return &memory.template access(instruction.data_segment(), target_address); + return &memory.template access(instruction.data_segment(), target_address); }; namespace Primitive { @@ -194,7 +196,7 @@ namespace Primitive { template void push(IntT &value, MemoryT &memory, RegistersT ®isters) { registers.sp_ -= sizeof(IntT); - memory.template access( + memory.template access( InstructionSet::x86::Source::SS, registers.sp_) = value; memory.template write_back(); @@ -202,7 +204,7 @@ void push(IntT &value, MemoryT &memory, RegistersT ®isters) { template IntT pop(MemoryT &memory, RegistersT ®isters) { - const auto value = memory.template access( + const auto value = memory.template access( 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(instruction, pointer, registers, memory); + source_address = address(instruction, pointer, registers, memory); break; case Source::IndirectNoBase: - source_address = address(instruction, pointer, registers, memory); + source_address = address(instruction, pointer, registers, memory); break; case Source::DirectAddress: - source_address = address(instruction, pointer, registers, memory); + source_address = address(instruction, pointer, registers, memory); break; } const Source source_segment = instruction.data_segment(); - const uint16_t offset = memory.template access(source_segment, source_address); + const uint16_t offset = memory.template access(source_segment, source_address); source_address += 2; - const uint16_t segment = memory.template access(source_segment, source_address); + const uint16_t segment = memory.template access(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(instruction, pointer, registers, memory); + source_address = address(instruction, pointer, registers, memory); break; case Source::IndirectNoBase: - source_address = address(instruction, pointer, registers, memory); + source_address = address(instruction, pointer, registers, memory); break; case Source::DirectAddress: - source_address = address(instruction, pointer, registers, memory); + source_address = address(instruction, pointer, registers, memory); break; } const Source source_segment = instruction.data_segment(); - const uint16_t offset = memory.template access(source_segment, source_address); + const uint16_t offset = memory.template access(source_segment, source_address); source_address += 2; - const uint16_t segment = memory.template access(source_segment, source_address); + const uint16_t segment = memory.template access(source_segment, source_address); flow_controller.jump(segment, offset); } @@ -930,14 +932,14 @@ void ld( RegistersT ®isters ) { const auto pointer = instruction.source(); - auto source_address = address(instruction, pointer, registers, memory); + auto source_address = address(instruction, pointer, registers, memory); const Source source_segment = instruction.data_segment(); - destination = memory.template access(source_segment, source_address); + destination = memory.template access(source_segment, source_address); source_address += 2; switch(selector) { - case Source::DS: registers.ds() = memory.template access(source_segment, source_address); break; - case Source::ES: registers.es() = memory.template access(source_segment, source_address); break; + case Source::DS: registers.ds() = memory.template access(source_segment, source_address); break; + case Source::ES: registers.es() = memory.template access(source_segment, source_address); break; } } @@ -949,7 +951,7 @@ void lea( RegistersT ®isters ) { // TODO: address size. - destination = IntT(address(instruction, instruction.source(), registers, memory)); + destination = IntT(address(instruction, instruction.source(), registers, memory)); } template @@ -963,7 +965,7 @@ void xlat( address = registers.bx() + registers.al(); } - registers.al() = memory.template access(instruction.data_segment(), address); + registers.al() = memory.template access(instruction.data_segment(), address); } template @@ -1403,8 +1405,8 @@ void cmps(const InstructionT &instruction, AddressT &eCX, AddressT &eSI, Address return; } - IntT lhs = memory.template access(instruction.data_segment(), eSI); - const IntT rhs = memory.template access(Source::ES, eDI); + IntT lhs = memory.template access(instruction.data_segment(), eSI); + const IntT rhs = memory.template access(Source::ES, eDI); eSI += status.direction() * sizeof(IntT); eDI += status.direction() * 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(Source::ES, eDI); + const IntT rhs = memory.template access(Source::ES, eDI); eDI += status.direction() * sizeof(IntT); Primitive::sub(eAX, rhs, status); @@ -1433,7 +1435,7 @@ void lods(const InstructionT &instruction, AddressT &eCX, AddressT &eSI, IntT &e return; } - eAX = memory.template access(instruction.data_segment(), eSI); + eAX = memory.template access(instruction.data_segment(), eSI); eSI += status.direction() * sizeof(IntT); repeat(status, eCX, flow_controller); @@ -1445,7 +1447,7 @@ void movs(const InstructionT &instruction, AddressT &eCX, AddressT &eSI, Address return; } - memory.template access(Source::ES, eDI) = memory.template access(instruction.data_segment(), eSI); + memory.template access(Source::ES, eDI) = memory.template access(instruction.data_segment(), eSI); eSI += status.direction() * sizeof(IntT); eDI += status.direction() * sizeof(IntT); @@ -1459,7 +1461,7 @@ void stos(AddressT &eCX, AddressT &eDI, IntT &eAX, MemoryT &memory, Status &stat return; } - memory.template access(Source::ES, eDI) = eAX; + memory.template access(Source::ES, eDI) = eAX; eDI += status.direction() * sizeof(IntT); repeat(status, eCX, flow_controller); @@ -1471,7 +1473,7 @@ void outs(const InstructionT &instruction, AddressT &eCX, uint16_t port, Address return; } - io.template out(port, memory.template access(instruction.data_segment(), eSI)); + io.template out(port, memory.template access(instruction.data_segment(), eSI)); eSI += status.direction() * sizeof(IntT); repeat(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(Source::ES, eDI) = io.template in(port); + memory.template access(Source::ES, eDI) = io.template in(port); eDI += status.direction() * sizeof(IntT); repeat(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( + return *resolve( instruction, instruction.source().source(), instruction.source(), @@ -1534,7 +1536,7 @@ template < &immediate); }; const auto destination = [&]() -> IntT& { - return *resolve( + return *resolve( instruction, instruction.destination().source(), instruction.destination(), diff --git a/OSBindings/Mac/Clock SignalTests/8088Tests.mm b/OSBindings/Mac/Clock SignalTests/8088Tests.mm index f89ceb692..224aeca93 100644 --- a/OSBindings/Mac/Clock SignalTests/8088Tests.mm +++ b/OSBindings/Mac/Clock SignalTests/8088Tests.mm @@ -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 IntT &access(InstructionSet::x86::Source segment, uint16_t address, Tag tag) { + template IntT &access(InstructionSet::x86::Source segment, uint16_t address, Tag tag) { const uint32_t physical_address = (segment_base(segment) + address) & 0xf'ffff; - return access(physical_address, tag); + return access(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 IntT &access(uint32_t address, Tag tag) { + template IntT &access(uint32_t address, Tag tag) { // Check for address wraparound if(address >= 0x10'0001 - sizeof(IntT)) { if constexpr (std::is_same_v) { @@ -167,7 +168,7 @@ struct Memory { } // Entry point for the 8086; simply notes that memory was accessed. - template IntT &access([[maybe_unused]] InstructionSet::x86::Source segment, uint32_t address) { + template IntT &access([[maybe_unused]] InstructionSet::x86::Source segment, uint32_t address) { if constexpr (std::is_same_v) { // 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(segment, address, Tag::Accessed); + return access(segment, address, Tag::Accessed); } template @@ -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(index) << 2; - const uint16_t new_ip = memory_.access(address, Memory::Tag::Accessed); - const uint16_t new_cs = memory_.access(address + 2, Memory::Tag::Accessed); + const uint16_t new_ip = memory_.access(address, Memory::Tag::Accessed); + const uint16_t new_cs = memory_.access(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( + memory_.access( InstructionSet::x86::Source::SS, registers_.sp_, is_flags ? Memory::Tag::FlagsH : Memory::Tag::Accessed ) = value >> 8; --registers_.sp_; - memory_.access( + memory_.access( InstructionSet::x86::Source::SS, registers_.sp_, is_flags ? Memory::Tag::FlagsL : Memory::Tag::Accessed