diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index 172d3e0c1..0a54aa4ee 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -1,6 +1,6 @@ name: SDL/Ubuntu -on: [push, pull_request] +on: [pull_request] jobs: build: @@ -10,7 +10,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Install dependencies - run: sudo apt-get --allow-releaseinfo-change update && sudo apt-get --fix-missing install libsdl2-dev scons + run: sudo apt-get --allow-releaseinfo-change update && sudo apt-get --fix-missing install gcc-10 libsdl2-dev scons - name: Make working-directory: OSBindings/SDL run: scons -j$(nproc --all) diff --git a/Analyser/Machines.hpp b/Analyser/Machines.hpp index bf4d408e7..0e801325f 100644 --- a/Analyser/Machines.hpp +++ b/Analyser/Machines.hpp @@ -25,6 +25,7 @@ enum class Machine { MasterSystem, MSX, Oric, + PCCompatible, Vic20, ZX8081, ZXSpectrum, diff --git a/InstructionSets/x86/Decoder.cpp b/InstructionSets/x86/Decoder.cpp index 4b6c788d9..6b8ccf718 100644 --- a/InstructionSets/x86/Decoder.cpp +++ b/InstructionSets/x86/Decoder.cpp @@ -15,7 +15,7 @@ using namespace InstructionSet::x86; template -std::pair::InstructionT> Decoder::decode(const uint8_t *source, size_t length) { +std::pair::InstructionT> Decoder::decode(const uint8_t *source, std::size_t length) { // Instruction length limits: // // 8086/80186: none* @@ -26,8 +26,8 @@ std::pair::InstructionT> Decoder::decode(con // be back to wherever it started, so it's safe to spit out a NOP and reset parsing // without any loss of context. This reduces the risk of the decoder tricking a caller into // an infinite loop. - constexpr int max_instruction_length = model >= Model::i80386 ? 15 : (model == Model::i80286 ? 10 : 65536); - const uint8_t *const end = source + std::min(length, size_t(max_instruction_length - consumed_)); + static constexpr std::size_t max_instruction_length = model >= Model::i80386 ? 15 : (model == Model::i80286 ? 10 : 65536); + const uint8_t *const end = source + std::min(length, max_instruction_length - consumed_); // MARK: - Prefixes (if present) and the opcode. @@ -58,14 +58,16 @@ std::pair::InstructionT> Decoder::decode(con #define RegAddr(op, dest, op_size, addr_size) \ SetOpSrcDestSize(op, DirectAddress, dest, op_size); \ displacement_size_ = addr_size; \ - phase_ = Phase::DisplacementOrOperand + phase_ = Phase::DisplacementOrOperand; \ + sign_extend_displacement_ = false /// Handles instructions of the form jjkk, Ax where the former is implicitly an address. #define AddrReg(op, source, op_size, addr_size) \ SetOpSrcDestSize(op, source, DirectAddress, op_size); \ displacement_size_ = addr_size; \ destination_ = Source::DirectAddress; \ - phase_ = Phase::DisplacementOrOperand + phase_ = Phase::DisplacementOrOperand; \ + sign_extend_displacement_ = false /// Covers both `mem/reg, reg` and `reg, mem/reg`. #define MemRegReg(op, format, size) \ @@ -1017,11 +1019,20 @@ std::pair::InstructionT> Decoder::decode(con if(bytes_to_consume == outstanding_bytes) { phase_ = Phase::ReadyToPost; - switch(displacement_size_) { - case DataSize::None: displacement_ = 0; break; - case DataSize::Byte: displacement_ = int8_t(inward_data_); break; - case DataSize::Word: displacement_ = int16_t(inward_data_); break; - case DataSize::DWord: displacement_ = int32_t(inward_data_); break; + if(!sign_extend_displacement_) { + switch(displacement_size_) { + case DataSize::None: displacement_ = 0; break; + case DataSize::Byte: displacement_ = uint8_t(inward_data_); break; + case DataSize::Word: displacement_ = uint16_t(inward_data_); break; + case DataSize::DWord: displacement_ = int32_t(inward_data_); break; + } + } else { + switch(displacement_size_) { + case DataSize::None: displacement_ = 0; break; + case DataSize::Byte: displacement_ = int8_t(inward_data_); break; + case DataSize::Word: displacement_ = int16_t(inward_data_); break; + case DataSize::DWord: displacement_ = int32_t(inward_data_); break; + } } inward_data_ >>= bit_size(displacement_size_); @@ -1112,3 +1123,7 @@ template class InstructionSet::x86::Decoder; template class InstructionSet::x86::Decoder; template class InstructionSet::x86::Decoder; template class InstructionSet::x86::Decoder; + +std::pair> Decoder8086::decode(const uint8_t *source, std::size_t length) { + return decoder.decode(source, length); +} diff --git a/InstructionSets/x86/Decoder.hpp b/InstructionSets/x86/Decoder.hpp index eaaad84e5..47ea4e6e2 100644 --- a/InstructionSets/x86/Decoder.hpp +++ b/InstructionSets/x86/Decoder.hpp @@ -38,13 +38,12 @@ template class Decoder { supplied cannot form a valid instruction. @discussion although instructions also contain an indicator of their length, on chips prior - to the 80286 there is no limit to instruction length and that could in theory overflow the available - storage, which can describe instructions only up to 1kb in size. + to the 80286 there is no limit to potential instruction length. The 80286 and 80386 have instruction length limits of 10 and 15 bytes respectively, so cannot overflow the field. */ - std::pair decode(const uint8_t *source, size_t length); + std::pair decode(const uint8_t *source, std::size_t length); /*! Enables or disables 32-bit protected mode. Meaningful only if the @c Model supports it. @@ -173,7 +172,7 @@ template class Decoder { // Ephemeral decoding state. Operation operation_ = Operation::Invalid; - int consumed_ = 0, operand_bytes_ = 0; + uint32_t consumed_ = 0, operand_bytes_ = 0; // Source and destination locations. Source source_ = Source::None; @@ -193,6 +192,8 @@ template class Decoder { DataSize operand_size_ = DataSize::None; // i.e. size of in-stream operand, if any. DataSize operation_size_ = DataSize::None; // i.e. size of data manipulated by the operation. + bool sign_extend_displacement_ = true; // If set then sign extend any displacement up to the address + // size; otherwise it'll be zero-padded. bool sign_extend_operand_ = false; // If set then sign extend the operand up to the operation size; // otherwise it'll be zero-padded. @@ -223,9 +224,23 @@ template class Decoder { next_inward_data_shift_ = 0; inward_data_ = 0; sign_extend_operand_ = false; + sign_extend_displacement_ = true; } }; +// This is a temporary measure; for reasons as-yet unknown, GCC isn't picking up the +// explicit instantiations of the template above at link time, even though is is +// unambiguously building and linking in Decoder.cpp. +// +// So here's a thin non-templated shim to unblock initial PC Compatible development. +class Decoder8086 { + public: + std::pair> decode(const uint8_t *source, std::size_t length); + + private: + Decoder decoder; +}; + } #endif /* InstructionSets_x86_Decoder_hpp */ diff --git a/InstructionSets/x86/Flags.hpp b/InstructionSets/x86/Flags.hpp index 599dd3203..d91ad0017 100644 --- a/InstructionSets/x86/Flags.hpp +++ b/InstructionSets/x86/Flags.hpp @@ -82,8 +82,8 @@ class Flags { using FlagT = uint32_t; // Flag getters. - template bool flag() const { - switch(flag) { + template bool flag() const { + switch(flag_v) { case Flag::Carry: return carry_; case Flag::AuxiliaryCarry: return auxiliary_carry_; case Flag::Sign: return sign_; @@ -163,7 +163,7 @@ class Flags { set_from(value & FlagValue::Interrupt); set_from(value & FlagValue::Direction); - set_from(value); + set_from(uint8_t(value)); set_from((~value) & FlagValue::Zero); set_from((~value) & FlagValue::Parity); diff --git a/InstructionSets/x86/Implementation/FlowControl.hpp b/InstructionSets/x86/Implementation/FlowControl.hpp index a247a42ed..63965cde6 100644 --- a/InstructionSets/x86/Implementation/FlowControl.hpp +++ b/InstructionSets/x86/Implementation/FlowControl.hpp @@ -13,6 +13,8 @@ #include "Stack.hpp" #include "../AccessType.hpp" +#include + namespace InstructionSet::x86::Primitive { template @@ -34,7 +36,7 @@ void jump( // TODO: proper behaviour in 32-bit. if(condition) { - context.flow_controller.jump(context.registers.ip() + displacement); + context.flow_controller.jump(uint16_t(context.registers.ip() + displacement)); } } @@ -76,7 +78,7 @@ void loopne( template void call_relative( - IntT offset, + typename std::make_signed::type offset, ContextT &context ) { push(context.registers.ip(), context); diff --git a/InstructionSets/x86/Implementation/PerformImplementation.hpp b/InstructionSets/x86/Implementation/PerformImplementation.hpp index 1d1422fe5..d40d99faa 100644 --- a/InstructionSets/x86/Implementation/PerformImplementation.hpp +++ b/InstructionSets/x86/Implementation/PerformImplementation.hpp @@ -175,8 +175,8 @@ template < case Operation::ESC: case Operation::NOP: return; - case Operation::AAM: Primitive::aam(context.registers.axp(), instruction.operand(), context); return; - case Operation::AAD: Primitive::aad(context.registers.axp(), instruction.operand(), context); return; + case Operation::AAM: Primitive::aam(context.registers.axp(), uint8_t(instruction.operand()), context); return; + case Operation::AAD: Primitive::aad(context.registers.axp(), uint8_t(instruction.operand()), context); return; case Operation::AAA: Primitive::aaas(context.registers.axp(), context); return; case Operation::AAS: Primitive::aaas(context.registers.axp(), context); return; case Operation::DAA: Primitive::daas(context.registers.al(), context); return; @@ -232,7 +232,9 @@ template < case Operation::NEG: Primitive::neg(source_rmw(), context); break; // TODO: should be a destination. case Operation::NOT: Primitive::not_(source_rmw()); break; // TODO: should be a destination. - case Operation::CALLrel: Primitive::call_relative(instruction.displacement(), context); return; + case Operation::CALLrel: + Primitive::call_relative(instruction.displacement(), context); + return; case Operation::CALLabs: Primitive::call_absolute(destination_r(), context); return; case Operation::CALLfar: Primitive::call_far(instruction, context); return; diff --git a/InstructionSets/x86/Implementation/Resolver.hpp b/InstructionSets/x86/Implementation/Resolver.hpp index 0f84eb825..f64239d6b 100644 --- a/InstructionSets/x86/Implementation/Resolver.hpp +++ b/InstructionSets/x86/Implementation/Resolver.hpp @@ -162,7 +162,7 @@ typename Accessor::type resolve( // // * if this is a memory access, set target_address and break; // * otherwise return the appropriate value. - uint32_t target_address; + uint32_t target_address = 0; switch(source) { // Defer all register accesses to the register-specific lookup. case Source::eAX: return *register_(context); @@ -183,7 +183,7 @@ typename Accessor::type resolve( case Source::None: return *none; case Source::Immediate: - *immediate = instruction.operand(); + *immediate = IntT(instruction.operand()); return *immediate; case Source::Indirect: diff --git a/Machines/PCCompatible/PCCompatible.cpp b/Machines/PCCompatible/PCCompatible.cpp new file mode 100644 index 000000000..53030ad27 --- /dev/null +++ b/Machines/PCCompatible/PCCompatible.cpp @@ -0,0 +1,438 @@ +// +// PCCompatible.cpp +// Clock Signal +// +// Created by Thomas Harte on 15/11/2023. +// Copyright © 2023 Thomas Harte. All rights reserved. +// + +#include "PCCompatible.hpp" + +#include "../../InstructionSets/x86/Decoder.hpp" +#include "../../InstructionSets/x86/Flags.hpp" +#include "../../InstructionSets/x86/Instruction.hpp" +#include "../../InstructionSets/x86/Perform.hpp" + +#include "../ScanProducer.hpp" +#include "../TimedMachine.hpp" + +#include + +namespace PCCompatible { + +struct Registers { + public: + static constexpr bool is_32bit = false; + + uint8_t &al() { return ax_.halves.low; } + uint8_t &ah() { return ax_.halves.high; } + uint16_t &ax() { return ax_.full; } + + CPU::RegisterPair16 &axp() { return ax_; } + + uint8_t &cl() { return cx_.halves.low; } + uint8_t &ch() { return cx_.halves.high; } + uint16_t &cx() { return cx_.full; } + + uint8_t &dl() { return dx_.halves.low; } + uint8_t &dh() { return dx_.halves.high; } + uint16_t &dx() { return dx_.full; } + + uint8_t &bl() { return bx_.halves.low; } + uint8_t &bh() { return bx_.halves.high; } + uint16_t &bx() { return bx_.full; } + + uint16_t &sp() { return sp_; } + uint16_t &bp() { return bp_; } + uint16_t &si() { return si_; } + uint16_t &di() { return di_; } + + uint16_t &ip() { return ip_; } + + uint16_t &es() { return es_; } + uint16_t &cs() { return cs_; } + uint16_t &ds() { return ds_; } + uint16_t &ss() { return ss_; } + uint16_t es() const { return es_; } + uint16_t cs() const { return cs_; } + uint16_t ds() const { return ds_; } + uint16_t ss() const { return ss_; } + + void reset() { + cs_ = 0xffff; + ip_ = 0; + } + + private: + CPU::RegisterPair16 ax_; + CPU::RegisterPair16 cx_; + CPU::RegisterPair16 dx_; + CPU::RegisterPair16 bx_; + + uint16_t sp_; + uint16_t bp_; + uint16_t si_; + uint16_t di_; + uint16_t es_, cs_, ds_, ss_; + uint16_t ip_; +}; + +class Segments { + public: + Segments(const Registers ®isters) : registers_(registers) {} + + using Source = InstructionSet::x86::Source; + + /// Posted by @c perform after any operation which *might* have affected a segment register. + void did_update(Source segment) { + switch(segment) { + default: break; + case Source::ES: es_base_ = uint32_t(registers_.es()) << 4; break; + case Source::CS: cs_base_ = uint32_t(registers_.cs()) << 4; break; + case Source::DS: ds_base_ = uint32_t(registers_.ds()) << 4; break; + case Source::SS: ss_base_ = uint32_t(registers_.ss()) << 4; break; + } + } + + void reset() { + did_update(Source::ES); + did_update(Source::CS); + did_update(Source::DS); + did_update(Source::SS); + } + + uint32_t es_base_, cs_base_, ds_base_, ss_base_; + + bool operator ==(const Segments &rhs) const { + return + es_base_ == rhs.es_base_ && + cs_base_ == rhs.cs_base_ && + ds_base_ == rhs.ds_base_ && + ss_base_ == rhs.ss_base_; + } + + private: + const Registers ®isters_; +}; + +// TODO: send writes to the ROM area off to nowhere. +struct Memory { + public: + using AccessType = InstructionSet::x86::AccessType; + + // Constructor. + Memory(Registers ®isters, const Segments &segments) : registers_(registers), segments_(segments) {} + + // + // Preauthorisation call-ins. Since only an 8088 is currently modelled, all accesses are implicitly authorised. + // + void preauthorise_stack_write([[maybe_unused]] uint32_t length) {} + void preauthorise_stack_read([[maybe_unused]] uint32_t length) {} + void preauthorise_read([[maybe_unused]] InstructionSet::x86::Source segment, [[maybe_unused]] uint16_t start, [[maybe_unused]] uint32_t length) {} + void preauthorise_read([[maybe_unused]] uint32_t start, [[maybe_unused]] uint32_t length) {} + + // + // Access call-ins. + // + + // Accesses an address based on segment:offset. + template + typename InstructionSet::x86::Accessor::type access(InstructionSet::x86::Source segment, uint16_t offset) { + const uint32_t physical_address = address(segment, offset); + + if constexpr (std::is_same_v) { + // If this is a 16-bit access that runs past the end of the segment, it'll wrap back + // to the start. So the 16-bit value will need to be a local cache. + if(offset == 0xffff) { + return split_word(physical_address, address(segment, 0)); + } + } + + return access(physical_address); + } + + // Accesses an address based on physical location. + template + typename InstructionSet::x86::Accessor::type access(uint32_t address) { + // Dispense with the single-byte case trivially. + if constexpr (std::is_same_v) { + return memory[address]; + } else if(address != 0xf'ffff) { + return *reinterpret_cast(&memory[address]); + } else { + return split_word(address, 0); + } + } + + template + void write_back() { + if constexpr (std::is_same_v) { + if(write_back_address_[0] != NoWriteBack) { + memory[write_back_address_[0]] = write_back_value_ & 0xff; + memory[write_back_address_[1]] = write_back_value_ >> 8; + write_back_address_[0] = 0; + } + } + } + + // + // Direct write. + // + template + void preauthorised_write(InstructionSet::x86::Source segment, uint16_t offset, IntT value) { + // Bytes can be written without further ado. + if constexpr (std::is_same_v) { + memory[address(segment, offset) & 0xf'ffff] = value; + return; + } + + // Words that straddle the segment end must be split in two. + if(offset == 0xffff) { + memory[address(segment, offset) & 0xf'ffff] = value & 0xff; + memory[address(segment, 0x0000) & 0xf'ffff] = value >> 8; + return; + } + + 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) { + memory[0xf'ffff] = value & 0xff; + memory[0x0'0000] = value >> 8; + return; + } + + // It's safe just to write then. + *reinterpret_cast(&memory[target]) = value; + } + + // + // Helper for instruction fetch. + // + std::pair next_code() { + const uint32_t start = segments_.cs_base_ + registers_.ip(); + return std::make_pair(&memory[start], 0x10'000 - start); + } + + std::pair all() { + return std::make_pair(memory.data(), 0x10'000); + } + + // + // Population. + // + void install(size_t address, const uint8_t *data, size_t length) { + std::copy(data, data + length, memory.begin() + std::vector::difference_type(address)); + } + + private: + std::array memory{0xff}; + Registers ®isters_; + const Segments &segments_; + + uint32_t segment_base(InstructionSet::x86::Source segment) { + using Source = InstructionSet::x86::Source; + switch(segment) { + default: return segments_.ds_base_; + case Source::ES: return segments_.es_base_; + case Source::CS: return segments_.cs_base_; + case Source::SS: return segments_.ss_base_; + } + } + + uint32_t address(InstructionSet::x86::Source segment, uint16_t offset) { + return (segment_base(segment) + offset) & 0xf'ffff; + } + + template + typename InstructionSet::x86::Accessor::type + split_word(uint32_t low_address, uint32_t high_address) { + if constexpr (is_writeable(type)) { + write_back_address_[0] = low_address; + write_back_address_[1] = high_address; + + // Prepopulate only if this is a modify. + if constexpr (type == AccessType::ReadModifyWrite) { + write_back_value_ = uint16_t(memory[write_back_address_[0]] | (memory[write_back_address_[1]] << 8)); + } + + return write_back_value_; + } else { + return uint16_t(memory[low_address] | (memory[high_address] << 8)); + } + } + + static constexpr uint32_t NoWriteBack = 0; // A low byte address of 0 can't require write-back. + uint32_t write_back_address_[2] = {NoWriteBack, NoWriteBack}; + uint16_t write_back_value_; +}; + +class IO { + public: + template void out([[maybe_unused]] uint16_t port, [[maybe_unused]] IntT value) { + switch(port) { + default: + if constexpr (std::is_same_v) { + printf("Unhandled out: %02x to %04x\n", value, port); + } else { + printf("Unhandled out: %04x to %04x\n", value, port); + } + break; + + // On the XT the NMI can be masked by setting bit 7 on I/O port 0xA0. + case 0x00a0: + printf("TODO: NMIs %s\n", (value & 0x80) ? "masked" : "unmasked"); + break; + } + } + template IntT in([[maybe_unused]] uint16_t port) { + printf("Unhandled in: %04x\n", port); + return IntT(~0); + } + + private: + +}; + +class FlowController { + public: + FlowController(Registers ®isters, Segments &segments) : + registers_(registers), segments_(segments) {} + + // Requirements for perform. + void jump(uint16_t address) { + registers_.ip() = address; + } + + void jump(uint16_t segment, uint16_t address) { + registers_.cs() = segment; + segments_.did_update(Segments::Source::CS); + registers_.ip() = address; + } + + void halt() {} + void wait() {} + + void repeat_last() { + should_repeat_ = true; + } + + // Other actions. + void begin_instruction() { + should_repeat_ = false; + } + bool should_repeat() const { + return should_repeat_; + } + + private: + Registers ®isters_; + Segments &segments_; + bool should_repeat_ = false; +}; + +class ConcreteMachine: + public Machine, + public MachineTypes::TimedMachine, + public MachineTypes::ScanProducer +{ + public: + ConcreteMachine( + [[maybe_unused]] const Analyser::Static::Target &target, + [[maybe_unused]] const ROMMachine::ROMFetcher &rom_fetcher + ) { + // This is actually a MIPS count; try 3 million. + set_clock_rate(3'000'000); + + // Fetch the BIOS. [8088 only, for now] + const auto bios = ROM::Name::PCCompatibleGLaBIOS; + + ROM::Request request = ROM::Request(bios); + auto roms = rom_fetcher(request); + if(!request.validate(roms)) { + throw ROMMachine::Error::MissingROMs; + } + + const auto &bios_contents = roms.find(bios)->second; + context.memory.install(0x10'0000 - bios_contents.size(), bios_contents.data(), bios_contents.size()); + } + + // MARK: - TimedMachine. + void run_for([[maybe_unused]] const Cycles cycles) override { + auto instructions = cycles.as_integral(); + while(instructions--) { + // Get the next thing to execute into decoded. + if(!context.flow_controller.should_repeat()) { + // Decode from the current IP. + const auto remainder = context.memory.next_code(); + decoded = decoder.decode(remainder.first, remainder.second); + + // If that didn't yield a whole instruction then the end of memory must have been hit; + // continue from the beginning. + if(decoded.first <= 0) { + const auto all = context.memory.all(); + decoded = decoder.decode(all.first, all.second); + } + + context.registers.ip() += decoded.first; + } else { + context.flow_controller.begin_instruction(); + } + + // Execute it. + InstructionSet::x86::perform( + decoded.second, + context + ); + } + } + + // MARK: - ScanProducer. + void set_scan_target([[maybe_unused]] Outputs::Display::ScanTarget *scan_target) override {} + Outputs::Display::ScanStatus get_scaled_scan_status() const override { + return Outputs::Display::ScanStatus(); + } + + private: + struct Context { + Context() : + segments(registers), + memory(registers, segments), + flow_controller(registers, segments) + { + reset(); + } + + void reset() { + registers.reset(); + segments.reset(); + } + + InstructionSet::x86::Flags flags; + Registers registers; + Segments segments; + Memory memory; + FlowController flow_controller; + IO io; + static constexpr auto model = InstructionSet::x86::Model::i8086; + } context; + + // TODO: eliminate use of Decoder8086 and Decoder8086 in gneral in favour of the templated version, as soon + // as whatever error is preventing GCC from picking up Decoder's explicit instantiations becomes apparent. + InstructionSet::x86::Decoder8086 decoder; +// InstructionSet::x86::Decoder decoder; + + std::pair> decoded; +}; + + +} + +using namespace PCCompatible; + +// See header; constructs and returns an instance of the Amstrad CPC. +Machine *Machine::PCCompatible(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) { + return new PCCompatible::ConcreteMachine(*target, rom_fetcher); +} + +Machine::~Machine() {} diff --git a/Machines/PCCompatible/PCCompatible.hpp b/Machines/PCCompatible/PCCompatible.hpp new file mode 100644 index 000000000..0362a414c --- /dev/null +++ b/Machines/PCCompatible/PCCompatible.hpp @@ -0,0 +1,33 @@ +// +// PCCompatible.hpp +// Clock Signal +// +// Created by Thomas Harte on 15/11/2023. +// Copyright © 2023 Thomas Harte. All rights reserved. +// + +#ifndef PCCompatible_hpp +#define PCCompatible_hpp + +#include "../../Analyser/Static/StaticAnalyser.hpp" +#include "../ROMMachine.hpp" + +namespace PCCompatible { + +/*! + Models a PC compatible. +*/ +class Machine { + public: + virtual ~Machine(); + + /// Creates and returns a PC Compatible. + static Machine *PCCompatible( + const Analyser::Static::Target *target, + const ROMMachine::ROMFetcher &rom_fetcher + ); +}; + +} + +#endif /* PCCompatible_hpp */ diff --git a/Machines/TimedMachine.hpp b/Machines/TimedMachine.hpp index 6c1580342..f634cccaa 100644 --- a/Machines/TimedMachine.hpp +++ b/Machines/TimedMachine.hpp @@ -13,7 +13,6 @@ #include "../ClockReceiver/TimeTypes.hpp" #include "AudioProducer.hpp" -#include "ScanProducer.hpp" #include diff --git a/Machines/Utility/MachineForTarget.cpp b/Machines/Utility/MachineForTarget.cpp index 8b0f3026a..8bde43eba 100644 --- a/Machines/Utility/MachineForTarget.cpp +++ b/Machines/Utility/MachineForTarget.cpp @@ -25,6 +25,7 @@ #include "../MasterSystem/MasterSystem.hpp" #include "../MSX/MSX.hpp" #include "../Oric/Oric.hpp" +#include "../PCCompatible/PCCompatible.hpp" #include "../Sinclair/ZX8081/ZX8081.hpp" #include "../Sinclair/ZXSpectrum/ZXSpectrum.hpp" @@ -69,6 +70,7 @@ Machine::DynamicMachine *Machine::MachineForTarget(const Analyser::Static::Targe Bind(Enterprise) Bind(MSX) Bind(Oric) + Bind(PCCompatible) BindD(Sega::MasterSystem, MasterSystem) BindD(Sinclair::ZX8081, ZX8081) BindD(Sinclair::ZXSpectrum, ZXSpectrum) @@ -139,6 +141,7 @@ std::string Machine::ShortNameForTargetMachine(const Analyser::Machine machine) case Analyser::Machine::MasterSystem: return "MasterSystem"; case Analyser::Machine::MSX: return "MSX"; case Analyser::Machine::Oric: return "Oric"; + case Analyser::Machine::PCCompatible: return "PCCompatible"; case Analyser::Machine::Vic20: return "Vic20"; case Analyser::Machine::ZX8081: return "ZX8081"; case Analyser::Machine::ZXSpectrum: return "ZXSpectrum"; @@ -162,6 +165,7 @@ std::string Machine::LongNameForTargetMachine(Analyser::Machine machine) { case Analyser::Machine::MasterSystem: return "Sega Master System"; case Analyser::Machine::MSX: return "MSX"; case Analyser::Machine::Oric: return "Oric"; + case Analyser::Machine::PCCompatible: return "PC Compatible"; case Analyser::Machine::Vic20: return "Vic 20"; case Analyser::Machine::ZX8081: return "ZX80/81"; case Analyser::Machine::ZXSpectrum: return "ZX Spectrum"; @@ -244,6 +248,7 @@ std::map> Machine::Target Add(Macintosh); Add(MSX); Add(Oric); + options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::PCCompatible), new Analyser::Static::Target(Analyser::Machine::PCCompatible))); AddMapped(Vic20, Commodore); Add(ZX8081); Add(ZXSpectrum); diff --git a/Machines/Utility/ROMCatalogue.cpp b/Machines/Utility/ROMCatalogue.cpp index 930a0d701..0d6a42f63 100644 --- a/Machines/Utility/ROMCatalogue.cpp +++ b/Machines/Utility/ROMCatalogue.cpp @@ -567,6 +567,8 @@ Description::Description(Name name) { case Name::OricMicrodisc: *this = Description(name, "Oric", "the Oric Microdisc ROM", "microdisc.rom", 8*1024, 0xa9664a9cu); break; case Name::Oric8DOSBoot: *this = Description(name, "Oric", "the 8DOS boot ROM", "8dos.rom", 512, 0x49a74c06u); break; + case Name::PCCompatibleGLaBIOS: *this = Description(name, "PCCompatible", "8088 GLaBIOS 0.2.5", "GLABIOS_0.2.5_8T.ROM", 8 * 1024, 0x9576944cu); break; + // TODO: CRCs below are incomplete, at best. case Name::MSXGenericBIOS: *this = Description(name, "MSX", "a generix MSX BIOS", "msx.rom", 32*1024, 0x94ee12f3u); break; case Name::MSXJapaneseBIOS: *this = Description(name, "MSX", "a Japanese MSX BIOS", "msx-japanese.rom", 32*1024, 0xee229390u); break; diff --git a/Machines/Utility/ROMCatalogue.hpp b/Machines/Utility/ROMCatalogue.hpp index 2580ad70c..54571bb98 100644 --- a/Machines/Utility/ROMCatalogue.hpp +++ b/Machines/Utility/ROMCatalogue.hpp @@ -132,6 +132,9 @@ enum Name { OricMicrodisc, Oric8DOSBoot, + // PCCompatible. + PCCompatibleGLaBIOS, + // Sinclair QL. SinclairQLJS, diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 248e9fc01..bb8eb4145 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -9,6 +9,8 @@ /* Begin PBXBuildFile section */ 423BDC4A2AB24699008E37B6 /* 8088Tests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 423BDC492AB24699008E37B6 /* 8088Tests.mm */; }; 42437B332AC70833006DFED1 /* HDV.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B6FD0342923061300EC4760 /* HDV.cpp */; }; + 425739382B051EA800B7D1E4 /* PCCompatible.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 425739372B051EA800B7D1E4 /* PCCompatible.cpp */; }; + 425739392B051EA800B7D1E4 /* PCCompatible.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 425739372B051EA800B7D1E4 /* PCCompatible.cpp */; }; 4281683A2A37AFB4008ECD27 /* DispatcherTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 428168392A37AFB4008ECD27 /* DispatcherTests.mm */; }; 42A5E80C2ABBE04600A0DD5D /* NeskellTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42A5E80B2ABBE04600A0DD5D /* NeskellTests.swift */; }; 42A5E8442ABBE16F00A0DD5D /* illegal_rmw_test.bin in Resources */ = {isa = PBXBuildFile; fileRef = 42A5E8332ABBE16F00A0DD5D /* illegal_rmw_test.bin */; }; @@ -1138,6 +1140,8 @@ 4257392E2AFBE2BC00B7D1E4 /* Repetition.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Repetition.hpp; sourceTree = ""; }; 4257392F2AFBE36B00B7D1E4 /* LoadStore.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = LoadStore.hpp; sourceTree = ""; }; 425739302AFBE47700B7D1E4 /* InOut.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = InOut.hpp; sourceTree = ""; }; + 425739362B051EA800B7D1E4 /* PCCompatible.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PCCompatible.hpp; sourceTree = ""; }; + 425739372B051EA800B7D1E4 /* PCCompatible.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PCCompatible.cpp; sourceTree = ""; }; 4281572E2AA0334300E16AA1 /* Carry.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Carry.hpp; sourceTree = ""; }; 428168372A16C25C008ECD27 /* LineLayout.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = LineLayout.hpp; sourceTree = ""; }; 428168392A37AFB4008ECD27 /* DispatcherTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DispatcherTests.mm; sourceTree = ""; }; @@ -2353,6 +2357,15 @@ path = Implementation; sourceTree = ""; }; + 425739352B051EA800B7D1E4 /* PCCompatible */ = { + isa = PBXGroup; + children = ( + 425739362B051EA800B7D1E4 /* PCCompatible.hpp */, + 425739372B051EA800B7D1E4 /* PCCompatible.cpp */, + ); + path = PCCompatible; + sourceTree = ""; + }; 42A5E8322ABBE16F00A0DD5D /* Neskell Tests */ = { isa = PBXGroup; children = ( @@ -4475,6 +4488,7 @@ 4B7F188B2154825D00388727 /* MasterSystem */, 4B79A4FC1FC8FF9800EEDAD5 /* MSX */, 4BCF1FA51DADC3E10039D2E7 /* Oric */, + 425739352B051EA800B7D1E4 /* PCCompatible */, 4B0F1BC92602F17B00B85C66 /* Sinclair */, 4B2B3A461F9B8FA70062DABF /* Utility */, ); @@ -5733,6 +5747,7 @@ 4BD424E62193B5830097291A /* Shader.cpp in Sources */, 4BC080CB26A238CC00D03FD8 /* AmigaADF.cpp in Sources */, 4B4B1A3D200198CA00A0F866 /* KonamiSCC.cpp in Sources */, + 425739392B051EA800B7D1E4 /* PCCompatible.cpp in Sources */, 4B055AC31FAE9AE80060FFFF /* AmstradCPC.cpp in Sources */, 4B055A9E1FAE85DA0060FFFF /* G64.cpp in Sources */, 4B055AB81FAE860F0060FFFF /* ZX80O81P.cpp in Sources */, @@ -5951,6 +5966,7 @@ 4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */, 4BDA00E422E663B900AC3CD0 /* NSData+CRC32.m in Sources */, 4B9EC0E626AA4A660060A31F /* Chipset.cpp in Sources */, + 425739382B051EA800B7D1E4 /* PCCompatible.cpp in Sources */, 4BB8616E24E22DC500A00E03 /* BufferingScanTarget.cpp in Sources */, 4BB4BFB022A42F290069048D /* MacintoshIMG.cpp in Sources */, 4B05401E219D1618001BF69C /* ScanTarget.cpp in Sources */, diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h index bcb958cb1..2028fb66f 100644 --- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h +++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h @@ -123,6 +123,10 @@ typedef NS_ENUM(NSInteger, CSMachineMSXRegion) { CSMachineMSXRegionJapanese, }; +typedef NS_ENUM(NSInteger, CSPCCompatibleModel) { + CSPCCompatibleModelTurboXT, +}; + typedef int Kilobytes; @interface CSStaticAnalyser : NSObject @@ -143,6 +147,7 @@ typedef int Kilobytes; - (instancetype)initWithVic20Region:(CSMachineVic20Region)region memorySize:(Kilobytes)memorySize hasC1540:(BOOL)hasC1540; - (instancetype)initWithZX80MemorySize:(Kilobytes)memorySize useZX81ROM:(BOOL)useZX81ROM; - (instancetype)initWithZX81MemorySize:(Kilobytes)memorySize; +- (instancetype)initWithPCCompatibleModel:(CSPCCompatibleModel)model; @property(nonatomic, readonly, nullable) NSString *optionsNibName; @property(nonatomic, readonly) NSString *displayName; diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm index dbca34373..bb7c8db8b 100644 --- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm +++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm @@ -272,6 +272,16 @@ return self; } +- (instancetype)initWithPCCompatibleModel:(CSPCCompatibleModel)model { + self = [super init]; + if(self) { + using Target = Analyser::Static::Target; + auto target = std::make_unique(Analyser::Machine::PCCompatible); + _targets.push_back(std::move(target)); + } + return self; +} + - (instancetype)initWithSpectrumModel:(CSMachineSpectrumModel)model { self = [super init]; if(self) { diff --git a/OSBindings/Mac/Clock Signal/MachinePicker/Base.lproj/MachinePicker.xib b/OSBindings/Mac/Clock Signal/MachinePicker/Base.lproj/MachinePicker.xib index c3733c142..8bac45227 100644 --- a/OSBindings/Mac/Clock Signal/MachinePicker/Base.lproj/MachinePicker.xib +++ b/OSBindings/Mac/Clock Signal/MachinePicker/Base.lproj/MachinePicker.xib @@ -1,8 +1,8 @@ - + - + @@ -17,10 +17,10 @@ - - + + - + - + @@ -59,16 +59,16 @@ Gw - + - + - + @@ -81,16 +81,16 @@ Gw - - + + - - + + @@ -98,7 +98,7 @@ Gw - + @@ -148,7 +148,7 @@ Gw - + @@ -172,7 +172,7 @@ Gw - + @@ -180,7 +180,7 @@ Gw - + @@ -235,19 +235,19 @@ Gw - + - - + + - - + + @@ -255,7 +255,7 @@ Gw - + @@ -269,7 +269,7 @@ Gw - + @@ -303,7 +303,7 @@ Gw - + @@ -338,32 +338,32 @@ Gw - +