mirror of
https://github.com/TomHarte/CLK.git
synced 2025-04-21 02:37:44 +00:00
Patch up enough to get an 80286 performer compilable.
This commit is contained in:
parent
d545cce276
commit
9df6d535e2
@ -16,12 +16,15 @@ namespace Analyser::Static::PCCompatible {
|
||||
struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Target> {
|
||||
ReflectableEnum(VideoAdaptor,
|
||||
MDA,
|
||||
CGA);
|
||||
CGA,
|
||||
);
|
||||
VideoAdaptor adaptor = VideoAdaptor::CGA;
|
||||
|
||||
ReflectableEnum(ModelApproximation,
|
||||
XT,
|
||||
TurboXT);
|
||||
TurboXT,
|
||||
AT
|
||||
);
|
||||
ModelApproximation model = ModelApproximation::TurboXT;
|
||||
|
||||
Target() : Analyser::Static::Target(Machine::PCCompatible) {}
|
||||
|
@ -240,11 +240,11 @@ void into(
|
||||
}
|
||||
}
|
||||
|
||||
template <typename IntT, typename InstructionT, typename ContextT>
|
||||
template <typename IntT, typename AddressT, typename InstructionT, typename ContextT>
|
||||
void bound(
|
||||
const InstructionT &instruction,
|
||||
read_t<IntT> destination,
|
||||
read_t<IntT> source,
|
||||
read_t<AddressT> destination,
|
||||
read_t<AddressT> source,
|
||||
ContextT &context
|
||||
) {
|
||||
using sIntT = typename std::make_signed<IntT>::type;
|
||||
@ -252,10 +252,9 @@ void bound(
|
||||
const auto source_segment = instruction.data_segment();
|
||||
context.memory.preauthorise_read(source_segment, source, 2*sizeof(IntT));
|
||||
const auto lower_bound =
|
||||
sIntT(context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source));
|
||||
source += 2;
|
||||
sIntT(context.memory.template access<IntT, AccessType::PreauthorisedRead>(source_segment, source));
|
||||
const auto upper_bound =
|
||||
sIntT(context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source));
|
||||
sIntT(context.memory.template access<IntT, AccessType::PreauthorisedRead>(source_segment, IntT(source + 2)));
|
||||
|
||||
if(sIntT(destination) < lower_bound || sIntT(destination) > upper_bound) {
|
||||
interrupt(Interrupt::BoundRangeExceeded, context);
|
||||
|
@ -223,7 +223,7 @@ template <
|
||||
} else {
|
||||
static_assert(int(Operation::IDIV_REP) == int(Operation::LEAVE));
|
||||
if constexpr (std::is_same_v<IntT, uint16_t> || std::is_same_v<IntT, uint32_t>) {
|
||||
Primitive::leave<IntT>();
|
||||
Primitive::leave<IntT>(context);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@ -338,7 +338,7 @@ template <
|
||||
break;
|
||||
} else {
|
||||
static_assert(int(Operation::SETMOC) == int(Operation::BOUND));
|
||||
Primitive::bound<IntT>(instruction, destination_r(), source_r(), context);
|
||||
Primitive::bound<IntT, AddressT>(instruction, destination_r(), source_r(), context);
|
||||
}
|
||||
return;
|
||||
|
||||
|
@ -174,7 +174,7 @@ void enter(
|
||||
context.registers.bp() -= 2;
|
||||
|
||||
const auto value = context.memory.template preauthorised_read<uint16_t>(Source::SS, context.registers.bp());
|
||||
push<uint16_t, true>(value);
|
||||
push<uint16_t, true>(value, context);
|
||||
}
|
||||
|
||||
// Set final BP.
|
||||
|
@ -850,7 +850,7 @@ public:
|
||||
|
||||
/// @returns The dynamic storage size argument supplied to an ENTER.
|
||||
constexpr ImmediateT dynamic_storage_size() const {
|
||||
return displacement();
|
||||
return offset();
|
||||
}
|
||||
|
||||
// Standard comparison operator.
|
||||
|
@ -39,7 +39,10 @@ struct Memory {
|
||||
|
||||
// Accesses an address based on segment:offset.
|
||||
template <typename IntT, AccessType type>
|
||||
typename InstructionSet::x86::Accessor<IntT, type>::type access(InstructionSet::x86::Source segment, uint16_t offset) {
|
||||
typename InstructionSet::x86::Accessor<IntT, type>::type access(
|
||||
const InstructionSet::x86::Source segment,
|
||||
const uint16_t offset
|
||||
) {
|
||||
const uint32_t physical_address = address(segment, offset);
|
||||
|
||||
if constexpr (std::is_same_v<IntT, uint16_t>) {
|
||||
@ -55,7 +58,7 @@ struct Memory {
|
||||
|
||||
// Accesses an address based on physical location.
|
||||
template <typename IntT, AccessType type>
|
||||
typename InstructionSet::x86::Accessor<IntT, type>::type access(uint32_t address) {
|
||||
typename InstructionSet::x86::Accessor<IntT, type>::type access(const uint32_t address) {
|
||||
// Dispense with the single-byte case trivially.
|
||||
if constexpr (std::is_same_v<IntT, uint8_t>) {
|
||||
return memory[address];
|
||||
@ -78,10 +81,14 @@ struct Memory {
|
||||
}
|
||||
|
||||
//
|
||||
// Direct write.
|
||||
// Direct read and write.
|
||||
//
|
||||
template <typename IntT>
|
||||
void preauthorised_write(InstructionSet::x86::Source segment, uint16_t offset, IntT value) {
|
||||
void preauthorised_write(
|
||||
const InstructionSet::x86::Source segment,
|
||||
const uint16_t offset,
|
||||
const IntT value
|
||||
) {
|
||||
// Bytes can be written without further ado.
|
||||
if constexpr (std::is_same_v<IntT, uint8_t>) {
|
||||
memory[address(segment, offset) & 0xf'ffff] = value;
|
||||
@ -105,7 +112,39 @@ struct Memory {
|
||||
}
|
||||
|
||||
// It's safe just to write then.
|
||||
*reinterpret_cast<uint16_t *>(&memory[target]) = value;
|
||||
*reinterpret_cast<IntT *>(&memory[target]) = value;
|
||||
}
|
||||
|
||||
template <typename IntT>
|
||||
IntT preauthorised_read(
|
||||
const InstructionSet::x86::Source segment,
|
||||
const uint16_t offset
|
||||
) {
|
||||
// Bytes can be written without further ado.
|
||||
if constexpr (std::is_same_v<IntT, uint8_t>) {
|
||||
return memory[address(segment, offset) & 0xf'ffff];
|
||||
}
|
||||
|
||||
// Words that straddle the segment end must be split in two.
|
||||
if(offset == 0xffff) {
|
||||
return IntT(
|
||||
memory[address(segment, offset) & 0xf'ffff] |
|
||||
memory[address(segment, 0x0000) & 0xf'ffff] << 8
|
||||
);
|
||||
}
|
||||
|
||||
const uint32_t target = address(segment, offset) & 0xf'ffff;
|
||||
|
||||
// Words that straddle the end of physical RAM must also be split in two.
|
||||
if(target == 0xf'ffff) {
|
||||
return IntT(
|
||||
memory[0xf'ffff] |
|
||||
memory[0x0'0000] << 8
|
||||
);
|
||||
}
|
||||
|
||||
// It's safe just to write then.
|
||||
return *reinterpret_cast<IntT *>(&memory[target]);
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -58,7 +58,8 @@ Log::Logger<Log::Source::PCCompatible> log;
|
||||
using PCModelApproximation = Analyser::Static::PCCompatible::Target::ModelApproximation;
|
||||
constexpr InstructionSet::x86::Model processor_model(PCModelApproximation model) {
|
||||
switch(model) {
|
||||
default: return InstructionSet::x86::Model::i8086;
|
||||
default: return InstructionSet::x86::Model::i8086;
|
||||
case PCModelApproximation::AT: return InstructionSet::x86::Model::i80286;
|
||||
}
|
||||
}
|
||||
|
||||
@ -907,25 +908,45 @@ class ConcreteMachine:
|
||||
set_clock_rate(double(pit_frequency));
|
||||
speaker_.speaker.set_input_rate(double(pit_frequency));
|
||||
|
||||
// Fetch the BIOS. [8088 only, for now]
|
||||
const auto bios = ROM::Name::PCCompatibleGLaBIOS;
|
||||
const auto tick = ROM::Name::PCCompatibleGLaTICK;
|
||||
// Fetch the BIOS.
|
||||
const auto font = Video::FontROM;
|
||||
|
||||
ROM::Request request = ROM::Request(bios) && ROM::Request(tick, true) && ROM::Request(font);
|
||||
constexpr auto biosXT = ROM::Name::PCCompatibleGLaBIOS;
|
||||
constexpr auto tickXT = ROM::Name::PCCompatibleGLaTICK;
|
||||
|
||||
constexpr auto biosAT = ROM::Name::PCCompatiblePhoenix80286BIOS;
|
||||
|
||||
ROM::Request request = ROM::Request(font);
|
||||
switch(pc_model) {
|
||||
default:
|
||||
request = request && ROM::Request(biosXT) && ROM::Request(tickXT);
|
||||
break;
|
||||
case PCModelApproximation::AT:
|
||||
request = request && ROM::Request(biosAT);
|
||||
break;
|
||||
}
|
||||
|
||||
auto roms = rom_fetcher(request);
|
||||
if(!request.validate(roms)) {
|
||||
throw ROMMachine::Error::MissingROMs;
|
||||
}
|
||||
|
||||
// A BIOS is mandatory.
|
||||
const auto &bios_contents = roms.find(bios)->second;
|
||||
context_.memory.install(0x10'0000 - bios_contents.size(), bios_contents.data(), bios_contents.size());
|
||||
switch(pc_model) {
|
||||
default: {
|
||||
const auto &bios_contents = roms.find(biosXT)->second;
|
||||
context_.memory.install(0x10'0000 - bios_contents.size(), bios_contents.data(), bios_contents.size());
|
||||
|
||||
// If found, install GlaTICK at 0xd'0000.
|
||||
auto tick_contents = roms.find(tick);
|
||||
if(tick_contents != roms.end()) {
|
||||
context_.memory.install(0xd'0000, tick_contents->second.data(), tick_contents->second.size());
|
||||
// If found, install GlaTICK at 0xd'0000.
|
||||
auto tick_contents = roms.find(tickXT);
|
||||
if(tick_contents != roms.end()) {
|
||||
context_.memory.install(0xd'0000, tick_contents->second.data(), tick_contents->second.size());
|
||||
}
|
||||
} break;
|
||||
|
||||
case PCModelApproximation::AT:
|
||||
const auto &bios_contents = roms.find(biosAT)->second;
|
||||
context_.memory.install(0x10'0000 - bios_contents.size(), bios_contents.data(), bios_contents.size());
|
||||
break;
|
||||
}
|
||||
|
||||
// Give the video card something to read from.
|
||||
@ -941,18 +962,9 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
// MARK: - TimedMachine.
|
||||
void run_for(const Cycles duration) override {
|
||||
using Model = Target::ModelApproximation;
|
||||
switch(pc_model) {
|
||||
case Model::XT: run_for<Model::XT>(duration); break;
|
||||
case Model::TurboXT: run_for<Model::TurboXT>(duration); break;
|
||||
}
|
||||
}
|
||||
|
||||
template <Target::ModelApproximation model>
|
||||
void run_for(const Cycles duration) {
|
||||
void run_for(const Cycles duration) final {
|
||||
const auto pit_ticks = duration.as<int>();
|
||||
constexpr bool is_fast = model == Target::ModelApproximation::TurboXT;
|
||||
constexpr bool is_fast = pc_model >= Target::ModelApproximation::TurboXT;
|
||||
|
||||
int ticks;
|
||||
if constexpr (is_fast) {
|
||||
@ -1087,15 +1099,15 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
// MARK: - ScanProducer.
|
||||
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override {
|
||||
void set_scan_target(Outputs::Display::ScanTarget *scan_target) final {
|
||||
video_.set_scan_target(scan_target);
|
||||
}
|
||||
Outputs::Display::ScanStatus get_scaled_scan_status() const override {
|
||||
Outputs::Display::ScanStatus get_scaled_scan_status() const final {
|
||||
return video_.get_scaled_scan_status();
|
||||
}
|
||||
|
||||
// MARK: - AudioProducer.
|
||||
Outputs::Speaker::Speaker *get_speaker() override {
|
||||
Outputs::Speaker::Speaker *get_speaker() final {
|
||||
return &speaker_.speaker;
|
||||
}
|
||||
|
||||
@ -1107,7 +1119,7 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
// MARK: - MediaTarget
|
||||
bool insert_media(const Analyser::Static::Media &media) override {
|
||||
bool insert_media(const Analyser::Static::Media &media) final {
|
||||
int c = 0;
|
||||
for(auto &disk : media.disks) {
|
||||
fdc_.set_disk(disk, c);
|
||||
@ -1118,11 +1130,11 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
// MARK: - MappedKeyboardMachine.
|
||||
MappedKeyboardMachine::KeyboardMapper *get_keyboard_mapper() override {
|
||||
MappedKeyboardMachine::KeyboardMapper *get_keyboard_mapper() final {
|
||||
return &keyboard_mapper_;
|
||||
}
|
||||
|
||||
void set_key_state(uint16_t key, bool is_pressed) override {
|
||||
void set_key_state(uint16_t key, bool is_pressed) final {
|
||||
keyboard_.post(uint8_t(key | (is_pressed ? 0x00 : 0x80)));
|
||||
}
|
||||
|
||||
@ -1132,25 +1144,25 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
// MARK: - Configuration options.
|
||||
std::unique_ptr<Reflection::Struct> get_options() const override {
|
||||
std::unique_ptr<Reflection::Struct> get_options() const final {
|
||||
auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
|
||||
options->output = get_video_signal_configurable();
|
||||
return options;
|
||||
}
|
||||
|
||||
void set_options(const std::unique_ptr<Reflection::Struct> &str) override {
|
||||
void set_options(const std::unique_ptr<Reflection::Struct> &str) final {
|
||||
const auto options = dynamic_cast<Options *>(str.get());
|
||||
set_video_signal_configurable(options->output);
|
||||
}
|
||||
|
||||
void set_display_type(Outputs::Display::DisplayType display_type) override {
|
||||
void set_display_type(Outputs::Display::DisplayType display_type) final {
|
||||
video_.set_display_type(display_type);
|
||||
|
||||
// Give the PPI a shout-out in case it isn't too late to switch to CGA40.
|
||||
ppi_handler_.hint_is_composite(Outputs::Display::is_composite(display_type));
|
||||
}
|
||||
|
||||
Outputs::Display::DisplayType get_display_type() const override {
|
||||
Outputs::Display::DisplayType get_display_type() const final {
|
||||
return video_.get_display_type();
|
||||
}
|
||||
|
||||
@ -1227,6 +1239,10 @@ std::unique_ptr<Machine> machine(const Target &target, const ROMMachine::ROMFetc
|
||||
case PCModelApproximation::TurboXT:
|
||||
return std::make_unique<PCCompatible::ConcreteMachine<video, PCModelApproximation::TurboXT>>
|
||||
(target, rom_fetcher);
|
||||
|
||||
case PCModelApproximation::AT:
|
||||
return std::make_unique<PCCompatible::ConcreteMachine<video, PCModelApproximation::AT>>
|
||||
(target, rom_fetcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user