diff --git a/Components/AY38910/AY38910.hpp b/Components/AY38910/AY38910.hpp index 4f7c226f0..f13a00810 100644 --- a/Components/AY38910/AY38910.hpp +++ b/Components/AY38910/AY38910.hpp @@ -169,19 +169,21 @@ template class AY38910: public ::Outputs::Speaker::SampleSource AY-deploying machines of the era. */ struct Utility { - template static void select_register(AY &ay, uint8_t reg) { - ay.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BDIR | GI::AY38910::BC2 | GI::AY38910::BC1)); - ay.set_data_input(reg); + template static void write(AY &ay, bool is_data_write, uint8_t data) { + ay.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BDIR | GI::AY38910::BC2 | (is_data_write ? 0 : GI::AY38910::BC1))); + ay.set_data_input(data); ay.set_control_lines(GI::AY38910::ControlLines(0)); } + template static void select_register(AY &ay, uint8_t reg) { + write(ay, false, reg); + } + template static void write_data(AY &ay, uint8_t reg) { - ay.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BDIR | GI::AY38910::BC2)); - ay.set_data_input(reg); - ay.set_control_lines(GI::AY38910::ControlLines(0)); + write(ay, true, reg); } - template static uint8_t read_data(AY &ay) { + template static uint8_t read(AY &ay) { ay.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BC2 | GI::AY38910::BC1)); const uint8_t result = ay.get_data_output(); ay.set_control_lines(GI::AY38910::ControlLines(0)); diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index 316a5d3f7..32c0647a3 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -1088,6 +1088,7 @@ template class ConcreteMachine: // If there are any tapes supplied, use the first of them. if(!media.tapes.empty()) { tape_player_.set_tape(media.tapes.front()); + set_use_fast_tape_hack(); } // Insert up to four disks. diff --git a/Machines/Atari/ST/AtariST.cpp b/Machines/Atari/ST/AtariST.cpp index 6e55a6d4b..17800a525 100644 --- a/Machines/Atari/ST/AtariST.cpp +++ b/Machines/Atari/ST/AtariST.cpp @@ -347,18 +347,11 @@ class ConcreteMachine: update_audio(); if(cycle.operation & Microcycle::Read) { - ay_.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BC2 | GI::AY38910::BC1)); - cycle.set_value8_high(ay_.get_data_output()); - ay_.set_control_lines(GI::AY38910::ControlLines(0)); + cycle.set_value8_high(GI::AY38910::Utility::read(ay_)); } else { // Net effect here: addresses with bit 1 set write to a register, // addresses with bit 1 clear select a register. - ay_.set_control_lines(GI::AY38910::ControlLines( - GI::AY38910::BC2 | GI::AY38910::BDIR - | ((address&2) ? 0 : GI::AY38910::BC1) - )); - ay_.set_data_input(cycle.value8_high()); - ay_.set_control_lines(GI::AY38910::ControlLines(0)); + GI::AY38910::Utility::write(ay_, address&2, cycle.value8_high()); } return delay + HalfCycles(2); diff --git a/Machines/ColecoVision/ColecoVision.cpp b/Machines/ColecoVision/ColecoVision.cpp index c8ebd9074..7f048d1ba 100644 --- a/Machines/ColecoVision/ColecoVision.cpp +++ b/Machines/ColecoVision/ColecoVision.cpp @@ -287,9 +287,7 @@ class ConcreteMachine: case 0x52: // Read AY data. update_audio(); - ay_.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BC2 | GI::AY38910::BC1)); - *cycle.value = ay_.get_data_output(); - ay_.set_control_lines(GI::AY38910::ControlLines(0)); + *cycle.value = GI::AY38910::Utility::read(ay_); break; } break; @@ -324,16 +322,12 @@ class ConcreteMachine: case 0x50: // Set AY address. update_audio(); - ay_.set_control_lines(GI::AY38910::BC1); - ay_.set_data_input(*cycle.value); - ay_.set_control_lines(GI::AY38910::ControlLines(0)); + GI::AY38910::Utility::select_register(ay_, *cycle.value); break; case 0x51: // Set AY data. update_audio(); - ay_.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BC2 | GI::AY38910::BDIR)); - ay_.set_data_input(*cycle.value); - ay_.set_control_lines(GI::AY38910::ControlLines(0)); + GI::AY38910::Utility::write_data(ay_, *cycle.value); break; case 0x53: super_game_module_.replace_ram = !!((*cycle.value)&0x1); diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index 2adc8f38b..4b1ca673d 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -525,9 +525,7 @@ class ConcreteMachine: case 0xa2: update_audio(); - ay_.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BC2 | GI::AY38910::BC1)); - *cycle.value = ay_.get_data_output(); - ay_.set_control_lines(GI::AY38910::ControlLines(0)); + *cycle.value = GI::AY38910::Utility::read(ay_); break; case 0xa8: case 0xa9: @@ -552,9 +550,7 @@ class ConcreteMachine: case 0xa0: case 0xa1: update_audio(); - ay_.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BDIR | GI::AY38910::BC2 | ((port == 0xa0) ? GI::AY38910::BC1 : 0))); - ay_.set_data_input(*cycle.value); - ay_.set_control_lines(GI::AY38910::ControlLines(0)); + GI::AY38910::Utility::write(ay_, port == 0xa1, *cycle.value); break; case 0xa8: case 0xa9: diff --git a/Machines/Sinclair/ZX8081/ZX8081.cpp b/Machines/Sinclair/ZX8081/ZX8081.cpp index c57e39f35..74eae2a16 100644 --- a/Machines/Sinclair/ZX8081/ZX8081.cpp +++ b/Machines/Sinclair/ZX8081/ZX8081.cpp @@ -472,22 +472,15 @@ template class ConcreteMachine: HalfCycles time_since_ay_update_; inline void ay_set_register(uint8_t value) { update_audio(); - ay_.set_control_lines(GI::AY38910::BC1); - ay_.set_data_input(value); - ay_.set_control_lines(GI::AY38910::ControlLines(0)); + GI::AY38910::Utility::select_register(ay_, value); } inline void ay_set_data(uint8_t value) { update_audio(); - ay_.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BC2 | GI::AY38910::BDIR)); - ay_.set_data_input(value); - ay_.set_control_lines(GI::AY38910::ControlLines(0)); + GI::AY38910::Utility::write_data(ay_, value); } inline uint8_t ay_read_data() { update_audio(); - ay_.set_control_lines(GI::AY38910::ControlLines(GI::AY38910::BC2 | GI::AY38910::BC1)); - const uint8_t value = ay_.get_data_output(); - ay_.set_control_lines(GI::AY38910::ControlLines(0)); - return value; + return GI::AY38910::Utility::read(ay_); } inline void update_audio() { speaker_.run_for(audio_queue_, time_since_ay_update_.divide_cycles(Cycles(2))); diff --git a/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp b/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp index c076d5090..11e03c647 100644 --- a/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp +++ b/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp @@ -335,7 +335,7 @@ template class ConcreteMachine: if((address & 0xc002) == 0xc000) { // Read from AY register. update_audio(); - *cycle.value &= GI::AY38910::Utility::read_data(ay_); + *cycle.value &= GI::AY38910::Utility::read(ay_); } // Check for a floating bus read; these are particularly arcane @@ -431,6 +431,7 @@ template class ConcreteMachine: // If there are any tapes supplied, use the first of them. if(!media.tapes.empty()) { tape_player_.set_tape(media.tapes.front()); + set_use_fast_tape(); } // Insert up to four disks. diff --git a/Processors/Z80/State/State.cpp b/Processors/Z80/State/State.cpp index bcba268f9..c9b90f2de 100644 --- a/Processors/Z80/State/State.cpp +++ b/Processors/Z80/State/State.cpp @@ -19,6 +19,7 @@ State::State(const ProcessorBase &src): State() { registers.bc = src.bc_.full; registers.de = src.de_.full; registers.hl = src.hl_.full; + registers.afDash = src.afDash_.full; registers.bcDash = src.bcDash_.full; registers.deDash = src.deDash_.full; registers.hlDash = src.hlDash_.full; @@ -107,6 +108,7 @@ void State::apply(ProcessorBase &target) { target.bc_.full = registers.bc; target.de_.full = registers.de; target.hl_.full = registers.hl; + target.afDash_.full = registers.afDash; target.bcDash_.full = registers.bcDash; target.deDash_.full = registers.deDash; target.hlDash_.full = registers.hlDash; @@ -177,6 +179,7 @@ State::Registers::Registers() { DeclareField(bc); DeclareField(de); DeclareField(hl); + DeclareField(afDash); DeclareField(bcDash); DeclareField(deDash); DeclareField(hlDash); diff --git a/Processors/Z80/State/State.hpp b/Processors/Z80/State/State.hpp index 3a9aa838f..f5676aa60 100644 --- a/Processors/Z80/State/State.hpp +++ b/Processors/Z80/State/State.hpp @@ -31,7 +31,7 @@ struct State: public Reflection::StructImpl { uint8_t a; uint8_t flags; uint16_t bc, de, hl; - uint16_t bcDash, deDash, hlDash; + uint16_t afDash, bcDash, deDash, hlDash; uint16_t ix, iy, ir; uint16_t program_counter, stack_pointer; uint16_t memptr; diff --git a/README.md b/README.md index 800358073..ba6d6cc21 100644 --- a/README.md +++ b/README.md @@ -24,15 +24,21 @@ It currently contains emulations of the: * Sinclair ZX80/81; and * Sinclair ZX Spectrum +2a/+3. -## Single-click Loading +## Single-step Loading Through static and runtime analysis CLK seeks automatically to select and configure the appropriate machine to run any provided disk, tape or ROM; to issue any commands necessary to run the software contained on the disk, tape or ROM; and to provide accelerated loading where feasible. -The full process of loading a title — even if you've never used the emulated machine before — is therefore: +With CLK installed the full process of loading a piece of software — even if you've never used the machine it runs on before — is therefore: 1. locate it in your OS; 2. double click it. +![Loading a piece of software](READMEImages/JustDoubleClick.gif) + +So there's no need to wade through creating a new machine, inserting media into it or figuring out which loading command goes with this piece of software, and no import procedure — CLK does not attempt to take ownership of your files or to usurp your OS. + +Keep your emulated titles on your desktop, in your dock, or wherever else you usually prefer to launch software from, and launch in a single step. Just like you'd expect from any other piece of desktop software. + ## Signal Processing Consider an ordinary, unmodified Commodore Vic-20. Its only video output is composite. Therefore the emulated machine's only video output is composite. In order to display the video output, your GPU must decode composite video. Therefore composite video artefacts are present and correct not because of a post hoc filter but because the real signal is really being processed. diff --git a/READMEImages/JustDoubleClick.gif b/READMEImages/JustDoubleClick.gif new file mode 100644 index 000000000..b90d97564 Binary files /dev/null and b/READMEImages/JustDoubleClick.gif differ diff --git a/Storage/Tape/Formats/ZXSpectrumTAP.cpp b/Storage/Tape/Formats/ZXSpectrumTAP.cpp index 080dcba11..5fa28c4de 100644 --- a/Storage/Tape/Formats/ZXSpectrumTAP.cpp +++ b/Storage/Tape/Formats/ZXSpectrumTAP.cpp @@ -38,7 +38,7 @@ ZXSpectrumTAP::ZXSpectrumTAP(const std::string &file_name) : } bool ZXSpectrumTAP::is_at_end() { - return file_.tell() == file_.stats().st_size; + return file_.tell() == file_.stats().st_size && phase_ == Phase::Gap; } void ZXSpectrumTAP::virtual_reset() { @@ -111,7 +111,7 @@ Tape::Pulse ZXSpectrumTAP::virtual_get_next_pulse() { } void ZXSpectrumTAP::read_next_block() { - if(is_at_end()) { + if(file_.tell() == file_.stats().st_size) { phase_ = Phase::Gap; } else { block_length_ = file_.get16le();