From 18d6197d6c17417ef1405b433a468136cfaae308 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 4 Apr 2021 22:20:35 -0400 Subject: [PATCH 1/6] Makes provision for pretty-printed key names. i.e. keys that don't fit C++ naming rules. --- Processors/Z80/State/State.cpp | 12 ++++++++++++ Processors/Z80/State/State.hpp | 2 ++ Reflection/Struct.cpp | 6 +++++- Reflection/Struct.hpp | 8 ++++++++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/Processors/Z80/State/State.cpp b/Processors/Z80/State/State.cpp index c9b90f2de..52b3e6022 100644 --- a/Processors/Z80/State/State.cpp +++ b/Processors/Z80/State/State.cpp @@ -195,6 +195,18 @@ State::Registers::Registers() { } } +std::unordered_map State::Registers::pretty_names() const { + return { + { "afDash", "af'" }, + { "bcDash", "bc'" }, + { "deDash", "de'" }, + { "hlDash", "hl'" }, + { "program_counter", "pc" }, + { "stack_pointer", "sp" }, + { "interrupt_mode", "im" }, + }; +} + State::ExecutionState::ExecutionState() { if(needs_declare()) { DeclareField(is_halted); diff --git a/Processors/Z80/State/State.hpp b/Processors/Z80/State/State.hpp index f5676aa60..d62156448 100644 --- a/Processors/Z80/State/State.hpp +++ b/Processors/Z80/State/State.hpp @@ -38,6 +38,8 @@ struct State: public Reflection::StructImpl { int interrupt_mode; bool iff1, iff2; + std::unordered_map pretty_names() const override; + Registers(); } registers; diff --git a/Reflection/Struct.cpp b/Reflection/Struct.cpp index 46a1c65a8..b27652301 100644 --- a/Reflection/Struct.cpp +++ b/Reflection/Struct.cpp @@ -205,6 +205,7 @@ void Reflection::Struct::append(std::ostringstream &stream, const std::string &k std::string Reflection::Struct::description() const { std::ostringstream stream; + const auto name_map = pretty_names(); stream << "{"; @@ -212,7 +213,10 @@ std::string Reflection::Struct::description() const { for(const auto &key: all_keys()) { if(!is_first) stream << ", "; is_first = false; - stream << key << ": "; + + // Use the pretty name for this key, if defined. + const auto mapped_key = name_map.find(key); + stream << (mapped_key != name_map.end() ? mapped_key->second : key) << ": "; const auto count = count_of(key); const auto type = type_of(key); diff --git a/Reflection/Struct.hpp b/Reflection/Struct.hpp index 24dbd489d..0e4cb3227 100644 --- a/Reflection/Struct.hpp +++ b/Reflection/Struct.hpp @@ -27,6 +27,14 @@ namespace Reflection { #define DeclareField(Name) declare(&Name, #Name) struct Struct { + /*! + Maps from internal key names to more human-friendly ones; e.g. might contain + a mapping from afDash to af'. + */ + virtual std::unordered_map pretty_names() const { + return {}; + } + virtual std::vector all_keys() const = 0; virtual const std::type_info *type_of(const std::string &name) const = 0; virtual size_t count_of(const std::string &name) const = 0; From 90c3d6a1e82b24da9ba6509fb43134bdd26fa9e0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 4 Apr 2021 22:39:30 -0400 Subject: [PATCH 2/6] Attempts a later interception of tape loading. --- Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp | 64 ++++++++++++++++----- 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp b/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp index 73f339027..0daac5a3e 100644 --- a/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp +++ b/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp @@ -39,6 +39,8 @@ #include "../../../ClockReceiver/JustInTime.hpp" +#include "../../../Processors/Z80/State/State.hpp" + #include "../Keyboard/Keyboard.hpp" #include @@ -186,6 +188,14 @@ template class ConcreteMachine: const uint16_t address = cycle.address ? *cycle.address : 0x0000; +// using Register = CPU::Z80::Register; +// if(cycle.operation == PartialMachineCycle::WriteStart && z80_.get_value_of_register(Register::ProgramCounter) == 0x5b1) { +// printf("%04x <- %02x\n", address, *cycle.value); +// } +// if(cycle.operation == PartialMachineCycle::ReadOpcodeStart && address == 0x0562) { +// printf("\n"); +// } + // Apply contention if necessary. if( is_contended_[address >> 14] && @@ -211,15 +221,15 @@ template class ConcreteMachine: case PartialMachineCycle::ReadOpcode: // Fast loading: ROM version. // - // The below patches over the 'LD-BYTES' routine from the 48kb ROM. - if(use_fast_tape_hack_ && address == 0x0556 && read_pointers_[0] == &rom_[0xc000]) { - if(perform_rom_ld_bytes()) { - // Stop pressing enter, if neccessry. - if(duration_to_press_enter_ > Cycles(0)) { - duration_to_press_enter_ = Cycles(0); - keyboard_.set_key_state(ZX::Keyboard::KeyEnter, false); - } + // The below patches over part of the 'LD-BYTES' routine from the 48kb ROM. + if(use_fast_tape_hack_ && address == 0x056b && read_pointers_[0] == &rom_[0xc000]) { + // Stop pressing enter, if neccessry. + if(duration_to_press_enter_ > Cycles(0)) { + duration_to_press_enter_ = Cycles(0); + keyboard_.set_key_state(ZX::Keyboard::KeyEnter, false); + } + if(perform_rom_ld_bytes_56b()) { *cycle.value = 0xc9; // i.e. RET. break; } @@ -627,31 +637,42 @@ template class ConcreteMachine: } // Reimplements the 'LD-BYTES' routine, as documented at - // https://skoolkid.github.io/rom/asm/0556.html i.e. + // https://skoolkid.github.io/rom/asm/0556.html but picking + // up from address 56b i.e. // // In: - // A: 0x00 or 0xff for block type; - // F: carry set if loading, clear if verifying; + // A': 0x00 or 0xff for block type; + // F': carry set if loading, clear if verifying; // DE: block length; // IX: start address. // // Out: // F: carry set for success, clear for error. - bool perform_rom_ld_bytes() { + // + // And, empirically: + // IX: one beyond final address written; + // DE: 0; + // L: parity byte; + // H: 0 for no error, 0xff for error; + // A: same as H. + // BC: ??? + bool perform_rom_ld_bytes_56b() { using Parser = Storage::Tape::ZXSpectrum::Parser; Parser parser(Parser::MachineType::ZXSpectrum); using Register = CPU::Z80::Register; - uint8_t flags = uint8_t(z80_.get_value_of_register(Register::Flags)); + uint8_t flags = uint8_t(z80_.get_value_of_register(Register::FlagsDash)); if(!(flags & 1)) return false; - const uint8_t block_type = uint8_t(z80_.get_value_of_register(Register::A)); + const uint8_t block_type = uint8_t(z80_.get_value_of_register(Register::ADash)); const auto block = parser.find_block(tape_player_.get_tape()); if(!block || block_type != (*block).type) return false; uint16_t length = z80_.get_value_of_register(Register::DE); uint16_t target = z80_.get_value_of_register(Register::IX); + flags = 0x93; + uint8_t parity = 0x00; while(length--) { auto next = parser.get_byte(tape_player_.get_tape()); if(!next) { @@ -660,10 +681,25 @@ template class ConcreteMachine: } write_pointers_[target >> 14][target] = *next; + parity ^= *next; ++target; } + auto stored_parity = parser.get_byte(tape_player_.get_tape()); + if(!stored_parity) { + flags &= ~1; + } else { + z80_.set_value_of_register(Register::L, *stored_parity); + } + z80_.set_value_of_register(Register::Flags, flags); + z80_.set_value_of_register(Register::DE, length); + z80_.set_value_of_register(Register::IX, target); + + const uint8_t h = (flags & 1) ? 0x00 : 0xff; + z80_.set_value_of_register(Register::H, h); + z80_.set_value_of_register(Register::A, h); + return true; } From 16bfe1a55c8cb846aaaff55eae5e134fd2ca0ebd Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 4 Apr 2021 22:45:56 -0400 Subject: [PATCH 3/6] Resolves use-after-return memory error. --- Machines/Sinclair/Keyboard/Keyboard.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Machines/Sinclair/Keyboard/Keyboard.cpp b/Machines/Sinclair/Keyboard/Keyboard.cpp index 686179835..1e9db2bf8 100644 --- a/Machines/Sinclair/Keyboard/Keyboard.cpp +++ b/Machines/Sinclair/Keyboard/Keyboard.cpp @@ -72,7 +72,7 @@ const uint16_t *CharacterMapper::sequence_for_character(char character) const { #define SHIFT(...) {KeyShift, __VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence} #define SYMSHIFT(...) {KeySymbolShift, __VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence} #define X {MachineTypes::MappedKeyboardMachine::KeyNotMapped} - constexpr KeySequence spectrum_key_sequences[] = { + static constexpr KeySequence spectrum_key_sequences[] = { /* NUL */ X, /* SOH */ X, /* STX */ X, /* ETX */ X, /* EOT */ X, /* ENQ */ X, @@ -137,7 +137,7 @@ const uint16_t *CharacterMapper::sequence_for_character(char character) const { /* z */ KEYS(KeyZ), }; - constexpr KeySequence zx81_key_sequences[] = { + static constexpr KeySequence zx81_key_sequences[] = { /* NUL */ X, /* SOH */ X, /* STX */ X, /* ETX */ X, /* EOT */ X, /* ENQ */ X, @@ -203,7 +203,7 @@ const uint16_t *CharacterMapper::sequence_for_character(char character) const { /* | */ X, /* } */ X, }; - static KeySequence zx80_key_sequences[] = { + static constexpr KeySequence zx80_key_sequences[] = { /* NUL */ X, /* SOH */ X, /* STX */ X, /* ETX */ X, /* EOT */ X, /* ENQ */ X, From fd88071c0abf7480b442816d49b3a41616092d04 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Apr 2021 17:21:26 -0400 Subject: [PATCH 4/6] Remove further detritus. --- Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp b/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp index 0daac5a3e..ef2aa417a 100644 --- a/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp +++ b/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp @@ -188,14 +188,6 @@ template class ConcreteMachine: const uint16_t address = cycle.address ? *cycle.address : 0x0000; -// using Register = CPU::Z80::Register; -// if(cycle.operation == PartialMachineCycle::WriteStart && z80_.get_value_of_register(Register::ProgramCounter) == 0x5b1) { -// printf("%04x <- %02x\n", address, *cycle.value); -// } -// if(cycle.operation == PartialMachineCycle::ReadOpcodeStart && address == 0x0562) { -// printf("\n"); -// } - // Apply contention if necessary. if( is_contended_[address >> 14] && From 76f2aba51ab609118ba863ee779a9c5bb302944c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Apr 2021 17:24:16 -0400 Subject: [PATCH 5/6] Makes use of pretty names in descriptions optional. --- Reflection/Struct.cpp | 10 +++++----- Reflection/Struct.hpp | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Reflection/Struct.cpp b/Reflection/Struct.cpp index b27652301..3e2a6157c 100644 --- a/Reflection/Struct.cpp +++ b/Reflection/Struct.cpp @@ -159,7 +159,7 @@ bool Reflection::fuzzy_set(Struct &target, const std::string &name, const std::s // MARK: - Description -void Reflection::Struct::append(std::ostringstream &stream, const std::string &key, const std::type_info *const type, size_t offset) const { +void Reflection::Struct::append(std::ostringstream &stream, const std::string &key, const std::type_info *const type, size_t offset, bool use_pretty_names) const { // Output Bools as yes/no. if(*type == typeid(bool)) { stream << ::Reflection::get(*this, key, offset); @@ -198,12 +198,12 @@ void Reflection::Struct::append(std::ostringstream &stream, const std::string &k // Recurse to deal with embedded objects. if(*type == typeid(Reflection::Struct)) { const Reflection::Struct *const child = reinterpret_cast(get(key)); - stream << child->description(); + stream << child->description(use_pretty_names); return; } } -std::string Reflection::Struct::description() const { +std::string Reflection::Struct::description(bool use_pretty_names) const { std::ostringstream stream; const auto name_map = pretty_names(); @@ -216,7 +216,7 @@ std::string Reflection::Struct::description() const { // Use the pretty name for this key, if defined. const auto mapped_key = name_map.find(key); - stream << (mapped_key != name_map.end() ? mapped_key->second : key) << ": "; + stream << (use_pretty_names && mapped_key != name_map.end() ? mapped_key->second : key) << ": "; const auto count = count_of(key); const auto type = type_of(key); @@ -226,7 +226,7 @@ std::string Reflection::Struct::description() const { } for(size_t index = 0; index < count; ++index) { - append(stream, key, type, index); + append(stream, key, type, index, use_pretty_names); if(index != count-1) stream << ", "; } diff --git a/Reflection/Struct.hpp b/Reflection/Struct.hpp index 0e4cb3227..14026f0af 100644 --- a/Reflection/Struct.hpp +++ b/Reflection/Struct.hpp @@ -50,7 +50,7 @@ struct Struct { @returns A string describing this struct. This string has no guaranteed layout, may not be sufficiently formed for a formal language parser, etc. */ - std::string description() const; + std::string description(bool use_pretty_names = true) const; /*! Serialises this struct in BSON format. @@ -83,7 +83,7 @@ struct Struct { virtual bool should_serialise([[maybe_unused]] const std::string &key) const { return true; } private: - void append(std::ostringstream &stream, const std::string &key, const std::type_info *type, size_t offset) const; + void append(std::ostringstream &stream, const std::string &key, const std::type_info *type, size_t offset, bool use_pretty_names) const; bool deserialise(const uint8_t *bson, size_t size); }; From 3e04b5112252d179ce2da745245cd57252df6dd8 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Apr 2021 17:26:18 -0400 Subject: [PATCH 6/6] Walks back pretty names. Probably a bad idea. --- Processors/Z80/State/State.cpp | 12 ------------ Processors/Z80/State/State.hpp | 2 -- Reflection/Struct.cpp | 13 +++++-------- Reflection/Struct.hpp | 12 ++---------- 4 files changed, 7 insertions(+), 32 deletions(-) diff --git a/Processors/Z80/State/State.cpp b/Processors/Z80/State/State.cpp index 52b3e6022..c9b90f2de 100644 --- a/Processors/Z80/State/State.cpp +++ b/Processors/Z80/State/State.cpp @@ -195,18 +195,6 @@ State::Registers::Registers() { } } -std::unordered_map State::Registers::pretty_names() const { - return { - { "afDash", "af'" }, - { "bcDash", "bc'" }, - { "deDash", "de'" }, - { "hlDash", "hl'" }, - { "program_counter", "pc" }, - { "stack_pointer", "sp" }, - { "interrupt_mode", "im" }, - }; -} - State::ExecutionState::ExecutionState() { if(needs_declare()) { DeclareField(is_halted); diff --git a/Processors/Z80/State/State.hpp b/Processors/Z80/State/State.hpp index d62156448..f5676aa60 100644 --- a/Processors/Z80/State/State.hpp +++ b/Processors/Z80/State/State.hpp @@ -38,8 +38,6 @@ struct State: public Reflection::StructImpl { int interrupt_mode; bool iff1, iff2; - std::unordered_map pretty_names() const override; - Registers(); } registers; diff --git a/Reflection/Struct.cpp b/Reflection/Struct.cpp index 3e2a6157c..babd12744 100644 --- a/Reflection/Struct.cpp +++ b/Reflection/Struct.cpp @@ -159,7 +159,7 @@ bool Reflection::fuzzy_set(Struct &target, const std::string &name, const std::s // MARK: - Description -void Reflection::Struct::append(std::ostringstream &stream, const std::string &key, const std::type_info *const type, size_t offset, bool use_pretty_names) const { +void Reflection::Struct::append(std::ostringstream &stream, const std::string &key, const std::type_info *const type, size_t offset) const { // Output Bools as yes/no. if(*type == typeid(bool)) { stream << ::Reflection::get(*this, key, offset); @@ -198,14 +198,13 @@ void Reflection::Struct::append(std::ostringstream &stream, const std::string &k // Recurse to deal with embedded objects. if(*type == typeid(Reflection::Struct)) { const Reflection::Struct *const child = reinterpret_cast(get(key)); - stream << child->description(use_pretty_names); + stream << child->description(); return; } } -std::string Reflection::Struct::description(bool use_pretty_names) const { +std::string Reflection::Struct::description() const { std::ostringstream stream; - const auto name_map = pretty_names(); stream << "{"; @@ -214,9 +213,7 @@ std::string Reflection::Struct::description(bool use_pretty_names) const { if(!is_first) stream << ", "; is_first = false; - // Use the pretty name for this key, if defined. - const auto mapped_key = name_map.find(key); - stream << (use_pretty_names && mapped_key != name_map.end() ? mapped_key->second : key) << ": "; + stream << key << ": "; const auto count = count_of(key); const auto type = type_of(key); @@ -226,7 +223,7 @@ std::string Reflection::Struct::description(bool use_pretty_names) const { } for(size_t index = 0; index < count; ++index) { - append(stream, key, type, index, use_pretty_names); + append(stream, key, type, index); if(index != count-1) stream << ", "; } diff --git a/Reflection/Struct.hpp b/Reflection/Struct.hpp index 14026f0af..24dbd489d 100644 --- a/Reflection/Struct.hpp +++ b/Reflection/Struct.hpp @@ -27,14 +27,6 @@ namespace Reflection { #define DeclareField(Name) declare(&Name, #Name) struct Struct { - /*! - Maps from internal key names to more human-friendly ones; e.g. might contain - a mapping from afDash to af'. - */ - virtual std::unordered_map pretty_names() const { - return {}; - } - virtual std::vector all_keys() const = 0; virtual const std::type_info *type_of(const std::string &name) const = 0; virtual size_t count_of(const std::string &name) const = 0; @@ -50,7 +42,7 @@ struct Struct { @returns A string describing this struct. This string has no guaranteed layout, may not be sufficiently formed for a formal language parser, etc. */ - std::string description(bool use_pretty_names = true) const; + std::string description() const; /*! Serialises this struct in BSON format. @@ -83,7 +75,7 @@ struct Struct { virtual bool should_serialise([[maybe_unused]] const std::string &key) const { return true; } private: - void append(std::ostringstream &stream, const std::string &key, const std::type_info *type, size_t offset, bool use_pretty_names) const; + void append(std::ostringstream &stream, const std::string &key, const std::type_info *type, size_t offset) const; bool deserialise(const uint8_t *bson, size_t size); };