From 39c0bc6c47e3b1b3745eda5ce45aee35a9ea59e5 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 13 May 2018 13:57:19 -0400 Subject: [PATCH 1/2] Factors string serialisation with \n\r conversion out of the Apple II and reuses it with the Oric. --- Machines/AppleII/AppleII.cpp | 32 ++++--------- Machines/Commodore/Vic-20/Vic20.cpp | 2 +- Machines/Oric/Oric.cpp | 20 ++++---- Machines/Utility/StringSerialiser.cpp | 46 +++++++++++++++++++ Machines/Utility/StringSerialiser.hpp | 30 ++++++++++++ .../Clock Signal.xcodeproj/project.pbxproj | 8 ++++ 6 files changed, 102 insertions(+), 36 deletions(-) create mode 100644 Machines/Utility/StringSerialiser.cpp create mode 100644 Machines/Utility/StringSerialiser.hpp diff --git a/Machines/AppleII/AppleII.cpp b/Machines/AppleII/AppleII.cpp index 56cbbbe10..6282d7a46 100644 --- a/Machines/AppleII/AppleII.cpp +++ b/Machines/AppleII/AppleII.cpp @@ -13,6 +13,7 @@ #include "../CRTMachine.hpp" #include "../KeyboardMachine.hpp" #include "../Utility/MemoryFuzzer.hpp" +#include "../Utility/StringSerialiser.hpp" #include "../../Processors/6502/6502.hpp" #include "../../Components/AudioToggle/AudioToggle.hpp" @@ -118,8 +119,7 @@ class ConcreteMachine: } // MARK - typing - std::string input_string_; - std::size_t input_string_pointer_ = std::numeric_limits::max(); + std::unique_ptr string_serialiser_; public: ConcreteMachine(): @@ -201,8 +201,8 @@ class ConcreteMachine: default: break; case 0xc000: - if(input_string_pointer_ != std::numeric_limits::max()) { - *value = static_cast(input_string_[input_string_pointer_]) | 0x80; + if(string_serialiser_) { + *value = string_serialiser_->head() | 0x80; } else { *value = keyboard_input_; } @@ -225,10 +225,9 @@ class ConcreteMachine: case 0xc010: keyboard_input_ &= 0x7f; - if(input_string_pointer_ != std::numeric_limits::max()) { - ++input_string_pointer_; - if(input_string_pointer_ == input_string_.size()) - input_string_pointer_ = std::numeric_limits::max(); + if(string_serialiser_) { + if(!string_serialiser_->advance()) + string_serialiser_.reset(); } break; @@ -361,22 +360,7 @@ class ConcreteMachine: } void type_string(const std::string &string) override { - input_string_.clear(); - input_string_.reserve(string.size()); - - // Commute any \ns that are not immediately after \rs to \rs; remove the rest. - bool saw_carriage_return = false; - for(auto character: string) { - if(character != '\n') { - input_string_.push_back(character); - } else { - if(!saw_carriage_return) { - input_string_.push_back('\r'); - } - } - saw_carriage_return = character == '\r'; - } - input_string_pointer_ = 0; + string_serialiser_.reset(new Utility::StringSerialiser(string, true)); } // MARK: ConfigurationTarget diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index d2066ec9e..87e90638a 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -659,7 +659,7 @@ class ConcreteMachine: user_port_via_.run_for(Cycles(1)); keyboard_via_.run_for(Cycles(1)); - if(typer_ && operation == CPU::MOS6502::BusOperation::ReadOpcode && address == 0xEB1E) { + if(typer_ && address == 0xeb1e && operation == CPU::MOS6502::BusOperation::ReadOpcode) { if(!typer_->type_next_character()) { clear_all_keys(); typer_.reset(); diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index b1d8b0a2e..81c9b1dbd 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -18,7 +18,7 @@ #include "../KeyboardMachine.hpp" #include "../Utility/MemoryFuzzer.hpp" -#include "../Utility/Typer.hpp" +#include "../Utility/StringSerialiser.hpp" #include "../../Processors/6502/6502.hpp" #include "../../Components/6522/6522.hpp" @@ -424,14 +424,10 @@ template class Co } } - if(typer_ && address == scan_keyboard_address_ && operation == CPU::MOS6502::BusOperation::ReadOpcode) { - // the Oric 1 misses any key pressed on the very first entry into the read keyboard routine, so don't - // do anything until at least the second, regardless of machine - if(!keyboard_read_count_) keyboard_read_count_++; - else if(!typer_->type_next_character()) { - clear_all_keys(); - typer_.reset(); - } + if(string_serialiser_ && address == scan_keyboard_address_ && operation == CPU::MOS6502::BusOperation::ReadOpcode) { + m6502_.set_value_of_register(CPU::MOS6502::Register::X, string_serialiser_->head() | 0x80); + *value = 0x60; + if(!string_serialiser_->advance()) string_serialiser_.reset(); } via_.run_for(Cycles(1)); @@ -490,8 +486,7 @@ template class Co // for Utility::TypeRecipient::Delegate void type_string(const std::string &string) override final { - std::unique_ptr mapper(new CharacterMapper); - Utility::TypeRecipient::add_typer(string, std::move(mapper)); + string_serialiser_.reset(new Utility::StringSerialiser(string, true)); } // for Microdisc::Delegate @@ -625,6 +620,9 @@ template class Co irq_line |= microdisc_.get_interrupt_request_line(); m6502_.set_irq_line(irq_line); } + + // MARK - typing + std::unique_ptr string_serialiser_; }; } diff --git a/Machines/Utility/StringSerialiser.cpp b/Machines/Utility/StringSerialiser.cpp new file mode 100644 index 000000000..ffbc97b58 --- /dev/null +++ b/Machines/Utility/StringSerialiser.cpp @@ -0,0 +1,46 @@ +// +// StringSerialiser.cpp +// Clock Signal +// +// Created by Thomas Harte on 13/05/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#include "StringSerialiser.hpp" + +using namespace Utility; + +StringSerialiser::StringSerialiser(const std::string &source, bool use_linefeed_only) { + if(!use_linefeed_only) { + input_string_ = source; + } else { + input_string_.reserve(source.size()); + + // Commute any \ns that are not immediately after \rs to \rs; remove the rest. + bool saw_carriage_return = false; + for(auto character: source) { + if(character != '\n') { + input_string_.push_back(character); + } else { + if(!saw_carriage_return) { + input_string_.push_back('\r'); + } + } + saw_carriage_return = character == '\r'; + } + } +} + +uint8_t StringSerialiser::head() { + if(input_string_pointer_ == input_string_.size()) + return '\0'; + return static_cast(input_string_[input_string_pointer_]); +} + +bool StringSerialiser::advance() { + if(input_string_pointer_ != input_string_.size()) { + ++input_string_pointer_; + return input_string_pointer_ != input_string_.size(); + } + return false; +} diff --git a/Machines/Utility/StringSerialiser.hpp b/Machines/Utility/StringSerialiser.hpp new file mode 100644 index 000000000..d08459aea --- /dev/null +++ b/Machines/Utility/StringSerialiser.hpp @@ -0,0 +1,30 @@ +// +// StringSerialiser.hpp +// Clock Signal +// +// Created by Thomas Harte on 13/05/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#ifndef StringSerialiser_hpp +#define StringSerialiser_hpp + +#include + +namespace Utility { + +class StringSerialiser { + public: + StringSerialiser(const std::string &source, bool use_linefeed_only = false); + + uint8_t head(); + bool advance(); + + private: + std::string input_string_; + std::size_t input_string_pointer_ = 0; +}; + +} + +#endif /* StringSerialiser_hpp */ diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index cab4b4feb..8ffd5537e 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -140,6 +140,8 @@ 4B15AA0E2082C799005E6C8D /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B15AA0A2082C799005E6C8D /* Video.cpp */; }; 4B15AA0F2082C799005E6C8D /* AppleII.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B15AA0C2082C799005E6C8D /* AppleII.cpp */; }; 4B15AA102082C799005E6C8D /* AppleII.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B15AA0C2082C799005E6C8D /* AppleII.cpp */; }; + 4B17B58B20A8A9D9007CCA8F /* StringSerialiser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B17B58920A8A9D9007CCA8F /* StringSerialiser.cpp */; }; + 4B17B58C20A8A9D9007CCA8F /* StringSerialiser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B17B58920A8A9D9007CCA8F /* StringSerialiser.cpp */; }; 4B1B88BB202E2EC100B67DFF /* MultiKeyboardMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1B88B9202E2EC100B67DFF /* MultiKeyboardMachine.cpp */; }; 4B1B88BC202E2EC100B67DFF /* MultiKeyboardMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1B88B9202E2EC100B67DFF /* MultiKeyboardMachine.cpp */; }; 4B1B88BD202E3D3D00B67DFF /* MultiMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3FCC3F201EC24200960631 /* MultiMachine.cpp */; }; @@ -740,6 +742,8 @@ 4B1667F91FFF215E00A16032 /* ASCII16kb.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ASCII16kb.hpp; path = MSX/Cartridges/ASCII16kb.hpp; sourceTree = ""; }; 4B1667FA1FFF215E00A16032 /* ASCII8kb.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ASCII8kb.hpp; path = MSX/Cartridges/ASCII8kb.hpp; sourceTree = ""; }; 4B1667FB1FFF215F00A16032 /* KonamiWithSCC.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = KonamiWithSCC.hpp; path = MSX/Cartridges/KonamiWithSCC.hpp; sourceTree = ""; }; + 4B17B58920A8A9D9007CCA8F /* StringSerialiser.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = StringSerialiser.cpp; sourceTree = ""; }; + 4B17B58A20A8A9D9007CCA8F /* StringSerialiser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = StringSerialiser.hpp; sourceTree = ""; }; 4B1B88B9202E2EC100B67DFF /* MultiKeyboardMachine.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MultiKeyboardMachine.cpp; sourceTree = ""; }; 4B1B88BA202E2EC100B67DFF /* MultiKeyboardMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MultiKeyboardMachine.hpp; sourceTree = ""; }; 4B1B88BE202E3DB200B67DFF /* MultiConfigurable.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MultiConfigurable.cpp; sourceTree = ""; }; @@ -1674,6 +1678,8 @@ 4B2B3A491F9B8FA70062DABF /* MemoryFuzzer.hpp */, 4B2B3A4A1F9B8FA70062DABF /* Typer.hpp */, 4B79A4FE1FC9082300EEDAD5 /* TypedDynamicMachine.hpp */, + 4B17B58920A8A9D9007CCA8F /* StringSerialiser.cpp */, + 4B17B58A20A8A9D9007CCA8F /* StringSerialiser.hpp */, ); path = Utility; sourceTree = ""; @@ -3672,6 +3678,7 @@ 4B15AA102082C799005E6C8D /* AppleII.cpp in Sources */, 4B055AA91FAE85EF0060FFFF /* CommodoreGCR.cpp in Sources */, 4B055ADB1FAE9B460060FFFF /* 6560.cpp in Sources */, + 4B17B58C20A8A9D9007CCA8F /* StringSerialiser.cpp in Sources */, 4B055AA01FAE85DA0060FFFF /* MFMSectorDump.cpp in Sources */, 4BEBFB522002DB30000708CC /* DiskROM.cpp in Sources */, 4B055AA11FAE85DA0060FFFF /* OricMFMDSK.cpp in Sources */, @@ -3838,6 +3845,7 @@ 4B2B3A4B1F9B8FA70062DABF /* Typer.cpp in Sources */, 4B4518821F75E91A00926311 /* PCMSegment.cpp in Sources */, 4B894522201967B4007DE474 /* StaticAnalyser.cpp in Sources */, + 4B17B58B20A8A9D9007CCA8F /* StringSerialiser.cpp in Sources */, 4BE7C9181E3D397100A5496D /* TIA.cpp in Sources */, 4B80AD001F85CACA00176895 /* BestEffortUpdater.cpp in Sources */, 4B2E2D9D1C3A070400138695 /* Electron.cpp in Sources */, From 2252c29495db852758a8742525ce16f0eedf1532 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 13 May 2018 14:02:46 -0400 Subject: [PATCH 2/2] Switches the Oric to string insertion at the time of consumption. To avoid issues around keyboard scanning being decoupled from consumption via IRQ, but not buffered. Keys pressed while BASIC is otherwise busy are just lost. --- Machines/Oric/Oric.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index 81c9b1dbd..cbc6fd4d4 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -305,13 +305,11 @@ template class Co switch(rom_type_) { case Analyser::Static::Oric::Target::ROM::BASIC10: tape_get_byte_address_ = 0xe630; - scan_keyboard_address_ = 0xf43c; tape_speed_address_ = 0x67; break; case Analyser::Static::Oric::Target::ROM::BASIC11: case Analyser::Static::Oric::Target::ROM::Pravetz: tape_get_byte_address_ = 0xe6c9; - scan_keyboard_address_ = 0xf495; tape_speed_address_ = 0x024d; break; } @@ -424,9 +422,12 @@ template class Co } } - if(string_serialiser_ && address == scan_keyboard_address_ && operation == CPU::MOS6502::BusOperation::ReadOpcode) { - m6502_.set_value_of_register(CPU::MOS6502::Register::X, string_serialiser_->head() | 0x80); - *value = 0x60; + // $02df is where the Oric ROMs — all of them, including BASIC 1.0, 1.1 and the Pravetz — have the + // IRQ routine store an incoming keystroke in order for reading to occur later. By capturing the + // read rather than the decode and write: (i) nothing is lost while BASIC is parsing; and + // (ii) keyboard input is much more rapid. + if(string_serialiser_ && address == 0x02df && operation == CPU::MOS6502::BusOperation::Read) { + *value = string_serialiser_->head() | 0x80; if(!string_serialiser_->advance()) string_serialiser_.reset(); } @@ -576,7 +577,7 @@ template class Co } // ROM bookkeeping - uint16_t tape_get_byte_address_ = 0, scan_keyboard_address_ = 0, tape_speed_address_ = 0; + uint16_t tape_get_byte_address_ = 0, tape_speed_address_ = 0; int keyboard_read_count_ = 0; // Outputs