// // Memory.hpp // Clock Signal // // Created by Thomas Harte on 01/12/2023. // Copyright © 2023 Thomas Harte. All rights reserved. // #pragma once #include "InstructionSets/x86/AccessType.hpp" #include "InstructionSets/x86/Descriptors.hpp" #include "InstructionSets/x86/Exceptions.hpp" #include "InstructionSets/x86/Model.hpp" #include "InstructionSets/x86/Registers.hpp" #include "LinearMemory.hpp" #include "Segments.hpp" #include namespace PCCompatible { template struct ProgramFetcher { std::pair next_code( const InstructionSet::x86::Registers ®isters, const Segments &segments, LinearMemoryT &linear_memory ) const { const uint16_t ip = registers.ip(); const auto &descriptor = segments.descriptors[InstructionSet::x86::Source::CS]; const uint32_t start = descriptor.to_linear(ip) & (linear_memory.MaxAddress - 1); return std::make_pair( linear_memory.at(start), std::min(linear_memory.MaxAddress - start, 1 + descriptor.bounds().end - ip) ); } std::pair start_code( const Segments &segments, LinearMemoryT &linear_memory ) const { const auto &descriptor = segments.descriptors[InstructionSet::x86::Source::CS]; const auto base = uint32_t(descriptor.base() & (linear_memory.MaxAddress - 1)); return std::make_pair( linear_memory.at(base), std::min(0x1'0000, 1 + descriptor.bounds().end - base) ); } }; template class SegmentedMemory; template class SegmentedMemory { private: static constexpr auto model = InstructionSet::x86::Model::i8086; public: using AccessType = InstructionSet::x86::AccessType; SegmentedMemory( InstructionSet::x86::Registers ®isters, const Segments &segments, LinearMemoryT &linear_memory ) : registers_(registers), segments_(segments), linear_memory_(linear_memory) {} // // Preauthorisation call-ins. // void preauthorise_stack_write(uint32_t, uint32_t) {} void preauthorise_stack_read(uint32_t, uint32_t) {} void preauthorise_read(InstructionSet::x86::Source, uint16_t, uint32_t) {} void preauthorise_write(InstructionSet::x86::Source, uint16_t, uint32_t) {} // // Access call-ins. // // Accesses an address based on segment:offset. template typename InstructionSet::x86::Accessor::type access( const InstructionSet::x86::Source segment, const uint16_t offset ) { const auto &descriptor = segments_.descriptors[segment]; return linear_memory_.template access(descriptor.to_linear(offset), descriptor.base()); } template void write_back() { linear_memory_.template write_back(); } template void preauthorised_write( const InstructionSet::x86::Source segment, const uint16_t offset, const IntT value ) { const auto &descriptor = segments_.descriptors[segment]; linear_memory_.template preauthorised_write(descriptor.to_linear(offset), descriptor.base(), value); } // // Helpers for instruction fetch. // std::pair next_code() const { return program_fetcher_.next_code(registers_, segments_, linear_memory_); } std::pair start_code() const { return program_fetcher_.start_code(segments_, linear_memory_); } private: InstructionSet::x86::Registers ®isters_; const Segments &segments_; LinearMemoryT &linear_memory_; ProgramFetcher program_fetcher_; }; template class SegmentedMemory { public: static constexpr auto model = InstructionSet::x86::Model::i80286; using Mode = InstructionSet::x86::Mode; using AccessType = InstructionSet::x86::AccessType; SegmentedMemory( InstructionSet::x86::Registers ®isters, const Segments &segments, LinearMemoryT &linear_memory ) : registers_(registers), segments_(segments), linear_memory_(linear_memory) {} // // Preauthorisation call-ins. // void preauthorise_stack_write(const uint32_t size, const uint32_t granularity) { const auto &descriptor = segments_.descriptors[InstructionSet::x86::Source::SS]; const auto leading_distance = registers_.sp(); if(leading_distance >= size) { descriptor.template authorise( uint16_t(registers_.sp() - size), registers_.sp() ); } else { // This stack write will overflow the segment; if there's an alignment issue then that's not // going to work. if(registers_.sp() % granularity) { descriptor.throw_gpf(); } // Authorise two blocks of writes. descriptor.template authorise( 0, registers_.sp() ); const auto remainder = size - leading_distance; descriptor.template authorise( uint16_t(65536 - remainder), 0 // i.e. 65536 ); } } void preauthorise_stack_read(const uint32_t size, const uint32_t granularity) { const auto &descriptor = segments_.descriptors[InstructionSet::x86::Source::SS]; const uint32_t trailing_distance = 65536 - registers_.sp(); if(trailing_distance >= size) { descriptor.template authorise( uint16_t(registers_.sp()), uint16_t(registers_.sp() + size) ); } else { // This stack write will overflow the segment; if there's an alignment issue then that's not // going to work. if(registers_.sp() % granularity) { descriptor.throw_gpf(); } // Authorise two blocks of reads. descriptor.template authorise( registers_.sp(), 0 // i.e. 65536 ); const uint32_t remainder = size - trailing_distance; descriptor.template authorise( 0, uint16_t(remainder) ); } } void preauthorise_read(const InstructionSet::x86::Source descriptor, const uint16_t offset, const uint32_t size) { segments_.descriptors[descriptor] .template authorise(offset, uint16_t(offset + size)); } void preauthorise_write(const InstructionSet::x86::Source descriptor, const uint16_t offset, const uint32_t size) { segments_.descriptors[descriptor] .template authorise(offset, uint16_t(offset + size)); } // TODO: perform authorisation checks. // // Access call-ins. // // Accesses an address based on segment:offset. template typename InstructionSet::x86::Accessor::type access( const InstructionSet::x86::Source segment, const uint16_t offset ) { const auto &descriptor = segments_.descriptors[segment]; descriptor.template authorise(offset, offset + sizeof(IntT)); return linear_memory_.template access(descriptor.to_linear(offset), descriptor.base()); } template void write_back() { linear_memory_.template write_back(); } template void preauthorised_write( const InstructionSet::x86::Source segment, const uint16_t offset, const IntT value ) { const auto &descriptor = segments_.descriptors[segment]; descriptor.template authorise(offset, offset + sizeof(IntT)); linear_memory_.template preauthorised_write(descriptor.to_linear(offset), descriptor.base(), value); } // // Mode selection. // void set_mode(const Mode mode) { mode_ = mode; } // // Helpers for instruction fetch. // std::pair next_code() const { return program_fetcher_.next_code(registers_, segments_, linear_memory_); } std::pair start_code() const { return program_fetcher_.start_code(segments_, linear_memory_); } private: InstructionSet::x86::Registers ®isters_; const Segments &segments_; LinearMemoryT &linear_memory_; ProgramFetcher program_fetcher_; Mode mode_ = Mode::Real; }; }