1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-08-05 08:26:28 +00:00

Attempt to reintroduce 80286 support (as was).

This commit is contained in:
Thomas Harte
2025-03-25 09:24:55 -04:00
parent fd31d07f41
commit e264375a97
4 changed files with 120 additions and 73 deletions

View File

@@ -205,7 +205,7 @@ struct LinearMemory<model, std::enable_if_t<model <= InstructionSet::x86::Model:
};
template <>
struct LinearMemory<InstructionSet::x86::Model::i80286>: public SplitHolder, public LinearPool<1 << 24> {
struct LinearMemory<InstructionSet::x86::Model::i80286>: public LinearPool<1 << 24> {
// A20 is the only thing that can cause split accesses on an 80286.
void set_a20_enabled(const bool enabled) {
address_mask_ = uint32_t(~0);
@@ -214,63 +214,25 @@ struct LinearMemory<InstructionSet::x86::Model::i80286>: public SplitHolder, pub
}
}
using AccessType = InstructionSet::x86::AccessType;
template <typename IntT, AccessType type>
typename InstructionSet::x86::Accessor<IntT, type>::type access(
uint32_t address, uint32_t
) {
// Bytes: always safe.
if constexpr (std::is_same_v<IntT, uint8_t>) {
return memory[address];
}
// Split on A20 if applicable.
if(((address + sizeof(IntT)) & address_mask_) < (address & address_mask_)) {
return SplitHolder::access<IntT, type>(address, (address + 1) & address_mask_, 1, memory.data());
}
// Don't split.
return *reinterpret_cast<IntT *>(&memory[address]);
// 80286: never split (probably?).
return *reinterpret_cast<IntT *>(&memory[address & address_mask_]);
}
template <typename IntT>
void write_back() {
SplitHolder::write_back<IntT>(memory.data());
}
void write_back() {}
template <typename IntT>
void preauthorised_write(
uint32_t,
uint32_t address,
const uint32_t,
IntT
IntT value
) {
// TODO: possibly split on A20.
// address &= MaxAddress - 1;
//
// // Bytes can be written without further ado.
// if constexpr (std::is_same_v<IntT, uint8_t>) {
// memory[address] = value;
// return;
// }
//
// // Words that straddle the segment end must be split in two.
// if constexpr (model == InstructionSet::x86::Model::i8086) {
// const uint32_t offset = address - base;
// if(offset == 0xffff) {
// memory[address] = uint8_t(value & 0xff);
// memory[base] = uint8_t(value >> 8);
// return;
// }
// }
//
// // Words that straddle the end of physical RAM must also be split in two.
// if(address == MaxAddress - 1) {
// memory[address] = uint8_t(value & 0xff);
// memory[0] = uint8_t(value >> 8);
// return;
// }
//
// // It's safe just to write then.
// *reinterpret_cast<IntT *>(&memory[address]) = value;
*reinterpret_cast<IntT *>(&memory[address & address_mask_]) = value;
}
private:

View File

@@ -959,11 +959,11 @@ private:
using namespace PCCompatible;
namespace {
//#ifndef NDEBUG
//static constexpr bool ForceAT = true;
//#else
#ifndef NDEBUG
static constexpr bool ForceAT = true;
#else
static constexpr bool ForceAT = false;
//#endif
#endif
template <Target::VideoAdaptor video>
std::unique_ptr<Machine> machine(const Target &target, const ROMMachine::ROMFetcher &rom_fetcher) {
@@ -976,8 +976,7 @@ std::unique_ptr<Machine> machine(const Target &target, const ROMMachine::ROMFetc
return std::make_unique<PCCompatible::ConcreteMachine<Model::TurboXT, video>>(target, rom_fetcher);
case Model::AT:
return nullptr;
// return std::make_unique<PCCompatible::ConcreteMachine<Model::AT, video>>(target, rom_fetcher);
return std::make_unique<PCCompatible::ConcreteMachine<Model::AT, video>>(target, rom_fetcher);
}
return nullptr;

View File

@@ -45,7 +45,8 @@ public:
uint16_t &si() { return si_; }
uint16_t &di() { return di_; }
uint16_t &ip() { return ip_; }
uint16_t &ip() { return ip_; }
uint16_t ip() const { return ip_; }
uint16_t &es() { return segments_[Source::ES]; }
uint16_t &cs() { return segments_[Source::CS]; }

View File

@@ -19,8 +19,35 @@
namespace PCCompatible {
template <InstructionSet::x86::Model model> class SegmentedMemory;
template <InstructionSet::x86::Model model> struct ProgramFetcher {
std::pair<const uint8_t *, size_t> next_code(
const Registers<model> &registers,
const Segments<model> &segments,
LinearMemory<model> &linear_memory
) const {
const uint16_t ip = registers.ip();
const uint32_t start =
segments.descriptors[InstructionSet::x86::Source::CS].to_linear(ip) & (linear_memory.MaxAddress - 1);
return std::make_pair(
linear_memory.at(start),
std::min<size_t>(linear_memory.MaxAddress - start, 0x10000 - ip)
);
}
std::pair<const uint8_t *, size_t> start_code(
const Segments<model> &segments,
LinearMemory<model> &linear_memory
) const {
const auto base = segments.descriptors[InstructionSet::x86::Source::CS].base();
return std::make_pair(
linear_memory.at(base),
std::min<size_t>(0x1'0000, linear_memory.MaxAddress - base)
);
}
};
template <InstructionSet::x86::Model model> class SegmentedMemory;
template <>
class SegmentedMemory<InstructionSet::x86::Model::i8086> {
@@ -77,42 +104,100 @@ public:
}
//
// Helper for instruction fetch.
// Helpers for instruction fetch.
//
std::pair<const uint8_t *, size_t> next_code() const {
const uint16_t ip = registers_.ip();
const uint32_t start = segments_.descriptors[InstructionSet::x86::Source::CS].to_linear(ip) & 0xf'ffff;
return std::make_pair(
linear_memory_.at(start),
std::min<size_t>(linear_memory_.MaxAddress - start, 0x10000 - ip)
);
return program_fetcher_.next_code(registers_, segments_, linear_memory_);
}
std::pair<const uint8_t *, size_t> start_code() const {
return std::make_pair(
linear_memory_.at(segments_.descriptors[InstructionSet::x86::Source::CS].base()),
0x1'0000
);
return program_fetcher_.start_code(segments_, linear_memory_);
}
private:
Registers<model> &registers_;
const Segments<model> &segments_;
LinearMemory<model> &linear_memory_;
ProgramFetcher<model> program_fetcher_;
};
template <>
struct SegmentedMemory<InstructionSet::x86::Model::i80286>:
public SegmentedMemory<InstructionSet::x86::Model::i8086> {
using SegmentedMemory<InstructionSet::x86::Model::i8086>::SegmentedMemory;
// TODO: almost everything.
struct SegmentedMemory<InstructionSet::x86::Model::i80286> {
static constexpr auto model = InstructionSet::x86::Model::i80286;
using Mode = InstructionSet::x86::Mode;
void set_mode(const Mode) {
using AccessType = InstructionSet::x86::AccessType;
SegmentedMemory(
Registers<model> &registers,
const Segments<model> &segments,
LinearMemory<model> &linear_memory
) : registers_(registers), segments_(segments), linear_memory_(linear_memory) {}
//
// Preauthorisation call-ins. Since only an 8088 is currently modelled, all accesses are implicitly authorised.
//
void preauthorise_stack_write(uint32_t) {}
void preauthorise_stack_read(uint32_t) {}
void preauthorise_read(InstructionSet::x86::Source, uint16_t, uint32_t) {}
void preauthorise_read(uint32_t, uint32_t) {}
void preauthorise_write(InstructionSet::x86::Source, uint16_t, uint32_t) {}
void preauthorise_write(uint32_t, uint32_t) {}
// 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];
return linear_memory_.access<IntT, type>(descriptor.to_linear(offset), descriptor.base());
}
template <typename IntT>
void write_back() {
linear_memory_.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_.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:
Registers<model> &registers_;
const Segments<model> &segments_;
LinearMemory<model> &linear_memory_;
ProgramFetcher<model> program_fetcher_;
Mode mode_ = Mode::Real;
};
//template <InstructionSet::x86::Model model>