1
0
mirror of https://github.com/TomHarte/CLK.git synced 2026-01-26 06:16:22 +00:00
Files
CLK/Machines/PCCompatible/SegmentedMemory.hpp
2025-10-29 09:19:04 -04:00

269 lines
8.3 KiB
C++

//
// 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 <algorithm>
namespace PCCompatible {
template <InstructionSet::x86::Model model, typename LinearMemoryT> struct ProgramFetcher {
std::pair<const uint8_t *, size_t> next_code(
const InstructionSet::x86::Registers<model> &registers,
const Segments<model, LinearMemoryT> &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<size_t>(linear_memory.MaxAddress - start, 1 + descriptor.bounds().end - ip)
);
}
std::pair<const uint8_t *, size_t> start_code(
const Segments<model, LinearMemoryT> &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<size_t>(0x1'0000, 1 + descriptor.bounds().end - base)
);
}
};
template <InstructionSet::x86::Model model, typename LinearMemoryT> class SegmentedMemory;
template <typename LinearMemoryT>
class SegmentedMemory<InstructionSet::x86::Model::i8086, LinearMemoryT> {
private:
static constexpr auto model = InstructionSet::x86::Model::i8086;
public:
using AccessType = InstructionSet::x86::AccessType;
SegmentedMemory(
InstructionSet::x86::Registers<model> &registers,
const Segments<model, LinearMemoryT> &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 IntT, AccessType type>
typename InstructionSet::x86::Accessor<IntT, type>::type access(
const InstructionSet::x86::Source segment,
const uint16_t offset
) {
const auto &descriptor = segments_.descriptors[segment];
return linear_memory_.template access<IntT, type>(descriptor.to_linear(offset), descriptor.base());
}
template <typename IntT>
void write_back() {
linear_memory_.template write_back<IntT>();
}
template <typename IntT>
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<IntT>(descriptor.to_linear(offset), descriptor.base(), value);
}
//
// Helpers for instruction fetch.
//
std::pair<const uint8_t *, size_t> next_code() const {
return program_fetcher_.next_code(registers_, segments_, linear_memory_);
}
std::pair<const uint8_t *, size_t> start_code() const {
return program_fetcher_.start_code(segments_, linear_memory_);
}
private:
InstructionSet::x86::Registers<model> &registers_;
const Segments<model, LinearMemoryT> &segments_;
LinearMemoryT &linear_memory_;
ProgramFetcher<model, LinearMemoryT> program_fetcher_;
};
template <typename LinearMemoryT>
class SegmentedMemory<InstructionSet::x86::Model::i80286, LinearMemoryT> {
public:
static constexpr auto model = InstructionSet::x86::Model::i80286;
using Mode = InstructionSet::x86::Mode;
using AccessType = InstructionSet::x86::AccessType;
SegmentedMemory(
InstructionSet::x86::Registers<model> &registers,
const Segments<model, LinearMemoryT> &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<InstructionSet::x86::AccessType::Write, uint16_t>(
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<InstructionSet::x86::AccessType::Write, uint16_t>(
0,
registers_.sp()
);
const auto remainder = size - leading_distance;
descriptor.template authorise<InstructionSet::x86::AccessType::Write, uint16_t>(
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<InstructionSet::x86::AccessType::Read, uint16_t>(
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<InstructionSet::x86::AccessType::Read, uint16_t>(
registers_.sp(),
0 // i.e. 65536
);
const uint32_t remainder = size - trailing_distance;
descriptor.template authorise<InstructionSet::x86::AccessType::Read, uint16_t>(
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<InstructionSet::x86::AccessType::Read, uint16_t>(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<InstructionSet::x86::AccessType::Write, uint16_t>(offset, uint16_t(offset + size));
}
// TODO: perform authorisation checks.
//
// Access call-ins.
//
// Accesses an address based on segment:offset.
template <typename IntT, AccessType type>
typename InstructionSet::x86::Accessor<IntT, type>::type access(
const InstructionSet::x86::Source segment,
const uint16_t offset
) {
const auto &descriptor = segments_.descriptors[segment];
descriptor.template authorise<type, uint16_t>(offset, offset + sizeof(IntT));
return linear_memory_.template access<IntT, type>(descriptor.to_linear(offset), descriptor.base());
}
template <typename IntT>
void write_back() {
linear_memory_.template write_back<IntT>();
}
template <typename IntT>
void preauthorised_write(
const InstructionSet::x86::Source segment,
const uint16_t offset,
const IntT value
) {
const auto &descriptor = segments_.descriptors[segment];
descriptor.template authorise<InstructionSet::x86::AccessType::Write, uint16_t>(offset, offset + sizeof(IntT));
linear_memory_.template preauthorised_write<IntT>(descriptor.to_linear(offset), descriptor.base(), value);
}
//
// Mode selection.
//
void set_mode(const Mode mode) {
mode_ = mode;
}
//
// Helpers for instruction fetch.
//
std::pair<const uint8_t *, size_t> next_code() const {
return program_fetcher_.next_code(registers_, segments_, linear_memory_);
}
std::pair<const uint8_t *, size_t> start_code() const {
return program_fetcher_.start_code(segments_, linear_memory_);
}
private:
InstructionSet::x86::Registers<model> &registers_;
const Segments<model, LinearMemoryT> &segments_;
LinearMemoryT &linear_memory_;
ProgramFetcher<model, LinearMemoryT> program_fetcher_;
Mode mode_ = Mode::Real;
};
}