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:
@@ -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:
|
||||
|
@@ -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;
|
||||
|
@@ -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]; }
|
||||
|
@@ -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> ®isters,
|
||||
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> ®isters_;
|
||||
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> ®isters,
|
||||
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> ®isters_;
|
||||
const Segments<model> &segments_;
|
||||
LinearMemory<model> &linear_memory_;
|
||||
ProgramFetcher<model> program_fetcher_;
|
||||
Mode mode_ = Mode::Real;
|
||||
};
|
||||
|
||||
//template <InstructionSet::x86::Model model>
|
||||
|
Reference in New Issue
Block a user