From 8db289e229d07461972360a1c6e5e65b57c66329 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 31 Jan 2021 13:12:59 -0500 Subject: [PATCH 01/11] Adds some notes-to-self on SCSI and a route to using Acorn's ADFS. --- Analyser/Static/Acorn/StaticAnalyser.cpp | 14 ++++-- Analyser/Static/Acorn/Target.hpp | 6 ++- Machines/Electron/Electron.cpp | 49 ++++++++++++++++--- .../StaticAnalyser/CSStaticAnalyser.mm | 2 +- ROMImages/Electron/readme.txt | 3 +- 5 files changed, 58 insertions(+), 16 deletions(-) diff --git a/Analyser/Static/Acorn/StaticAnalyser.cpp b/Analyser/Static/Acorn/StaticAnalyser.cpp index 76d655cfc..e37ffc7c8 100644 --- a/Analyser/Static/Acorn/StaticAnalyser.cpp +++ b/Analyser/Static/Acorn/StaticAnalyser.cpp @@ -61,10 +61,6 @@ static std::vector> Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) { auto target = std::make_unique(); - target->confidence = 0.5; // TODO: a proper estimation - target->has_dfs = false; - target->has_adfs = false; - target->should_shift_restart = false; // strip out inappropriate cartridges target->media.cartridges = AcornCartridgesFrom(media.cartridges); @@ -111,9 +107,10 @@ Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &me if(dfs_catalogue == nullptr) adfs_catalogue = GetADFSCatalogue(disk); if(dfs_catalogue || adfs_catalogue) { // Accept the disk and determine whether DFS or ADFS ROMs are implied. + // Use the Pres ADFS if using an ADFS, as it leaves Page at &EOO. target->media.disks = media.disks; target->has_dfs = bool(dfs_catalogue); - target->has_adfs = bool(adfs_catalogue); + target->has_pres_adfs = bool(adfs_catalogue); // Check whether a simple shift+break will do for loading this disk. Catalogue::BootOption bootOption = (dfs_catalogue ?: adfs_catalogue)->bootOption; @@ -144,6 +141,13 @@ Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &me } } + // Enable the Acorn ADFS if a mass-storage device is attached; + // unlike the Pres ADFS it retains SCSI logic. + if(!media.mass_storage_devices.empty()) { + target->has_pres_adfs = false; + target->has_acorn_adfs = true; + } + TargetList targets; if(!target->media.empty()) { targets.push_back(std::move(target)); diff --git a/Analyser/Static/Acorn/Target.hpp b/Analyser/Static/Acorn/Target.hpp index f415f4b30..17dad7393 100644 --- a/Analyser/Static/Acorn/Target.hpp +++ b/Analyser/Static/Acorn/Target.hpp @@ -18,7 +18,8 @@ namespace Static { namespace Acorn { struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl { - bool has_adfs = false; + bool has_acorn_adfs = false; + bool has_pres_adfs = false; bool has_dfs = false; bool has_ap6_rom = false; bool has_sideways_ram = false; @@ -27,7 +28,8 @@ struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl< Target() : Analyser::Static::Target(Machine::Electron) { if(needs_declare()) { - DeclareField(has_adfs); + DeclareField(has_pres_adfs); + DeclareField(has_acorn_adfs); DeclareField(has_dfs); DeclareField(has_ap6_rom); DeclareField(has_sideways_ram); diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 4344e44a4..ad148b88f 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -64,10 +64,15 @@ class ConcreteMachine: {machine_name, "the Acorn BASIC II ROM", "basic.rom", 16*1024, 0x79434781}, {machine_name, "the Electron MOS ROM", "os.rom", 16*1024, 0xbf63fb1f} }; - if(target.has_adfs) { + const size_t pres_adfs_rom_position = required_roms.size(); + if(target.has_pres_adfs) { required_roms.emplace_back(machine_name, "the E00 ADFS ROM, first slot", "ADFS-E00_1.rom", 16*1024, 0x51523993); required_roms.emplace_back(machine_name, "the E00 ADFS ROM, second slot", "ADFS-E00_2.rom", 16*1024, 0x8d17de0e); } + const size_t acorn_adfs_rom_position = required_roms.size(); + if(target.has_acorn_adfs) { + required_roms.emplace_back(machine_name, "the Acorn ADFS ROM", "adfs.rom", 16*1024, 0x3289bdc6); + } const size_t dfs_rom_position = required_roms.size(); if(target.has_dfs) { required_roms.emplace_back(machine_name, "the 1770 DFS ROM", "DFS-1770-2.20.rom", 16*1024, 0xf3dc9bc5); @@ -91,19 +96,23 @@ class ConcreteMachine: * the keyboard and BASIC ROMs occupy slots 8, 9, 10 and 11; * the DFS, if in use, occupies slot 1; - * the ADFS, if in use, occupies slots 4 and 5; + * the Pres ADFS, if in use, occupies slots 4 and 5; + * the Acorn ADFS, if in use, occupies slot 6; * the AP6, if in use, occupies slot 15; and * if sideways RAM was asked for, all otherwise unused slots are populated with sideways RAM. */ - if(target.has_dfs || target.has_adfs) { + if(target.has_dfs || target.has_acorn_adfs || target.has_pres_adfs) { plus3_ = std::make_unique(); if(target.has_dfs) { set_rom(ROM::Slot0, *roms[dfs_rom_position], true); } - if(target.has_adfs) { - set_rom(ROM::Slot4, *roms[2], true); - set_rom(ROM::Slot5, *roms[3], true); + if(target.has_pres_adfs) { + set_rom(ROM::Slot4, *roms[pres_adfs_rom_position], true); + set_rom(ROM::Slot5, *roms[pres_adfs_rom_position+1], true); + } + if(target.has_acorn_adfs) { + set_rom(ROM::Slot6, *roms[acorn_adfs_rom_position], true); } } @@ -298,6 +307,7 @@ class ConcreteMachine: break; case 0xfc04: case 0xfc05: case 0xfc06: case 0xfc07: + printf("%04x %s %02x\n", address, isReadOperation(operation) ? "->" : "<-", *value); if(plus3_ && (address&0x00f0) == 0x00c0) { if(is_holding_shift_ && address == 0xfcc4) { is_holding_shift_ = false; @@ -310,12 +320,39 @@ class ConcreteMachine: } break; case 0xfc00: + printf("%04x %s %02x\n", address, isReadOperation(operation) ? "->" : "<-", *value); if(plus3_ && (address&0x00f0) == 0x00c0) { if(!isReadOperation(operation)) { plus3_->set_control_register(*value); } else *value = 1; } break; + case 0xfc03: + printf("%04x %s %02x\n", address, isReadOperation(operation) ? "->" : "<-", *value); + break; + + // SCSI locations: + // + // fc40: data, read and write + // fc41: status read + // fc42: select write + // fc43: interrupt latch + // + // Status byte is: + // + // b7: SCSI C/D + // b6: SCSI I/O + // b5: SCSI REQ + // b4: interrupt flag + // b3: 0 + // b2: 0 + // b1: SCSI BSY + // b0: SCSI MSG + // + // Interrupt latch is: + // + // b0: enable or disable IRQ on REQ + // (and, possibly, writing to the latch acknowledges?) default: if(address >= 0xc000) { diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm index 71eb87247..a302b5f3c 100644 --- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm +++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm @@ -99,7 +99,7 @@ using Target = Analyser::Static::Acorn::Target; auto target = std::make_unique(); target->has_dfs = dfs; - target->has_adfs = adfs; + target->has_pres_adfs = adfs; target->has_ap6_rom = ap6; target->has_sideways_ram = sidewaysRAM; _targets.push_back(std::move(target)); diff --git a/ROMImages/Electron/readme.txt b/ROMImages/Electron/readme.txt index 995145ee9..349144e2a 100644 --- a/ROMImages/Electron/readme.txt +++ b/ROMImages/Electron/readme.txt @@ -8,10 +8,9 @@ DFS-1770-2.20.rom — used only if the user opens a DFS disk image ADFS-E00_1.rom — used only if the user opens an ADFS disk image ADFS-E00_2.rom AP6v133.rom — used only if the user opens a disk image that makes use of any of the commands given below. +adfs.rom - used only if the user opens a hard disk image Possibly to be desired in the future: -* adfs.rom -* ElectronExpansionRomPresAP2-v1.23.rom * os300.rom Commands that trigger a request for the AP6v133 ROM: From f1ba040dd84474a430f489d34559a9f8d72b6713 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 31 Jan 2021 16:00:52 -0500 Subject: [PATCH 02/11] This is probably how Acorn hard disk images look (?) --- .../Clock Signal.xcodeproj/project.pbxproj | 6 +++ Storage/MassStorage/Formats/AcornADF.cpp | 36 +++++++++++++++++ Storage/MassStorage/Formats/AcornADF.hpp | 39 +++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 Storage/MassStorage/Formats/AcornADF.cpp create mode 100644 Storage/MassStorage/Formats/AcornADF.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 92ace4adc..761c00ae3 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -850,6 +850,7 @@ 4BE211DE253E4E4800435408 /* 65C02_no_Rockwell_test.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4BE211DD253E4E4800435408 /* 65C02_no_Rockwell_test.bin */; }; 4BE34438238389E10058E78F /* AtariSTVideoTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE34437238389E10058E78F /* AtariSTVideoTests.mm */; }; 4BE76CF922641ED400ACD6FA /* QLTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE76CF822641ED300ACD6FA /* QLTests.mm */; }; + 4BE8EB6625C750B50040BC40 /* AcornADF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE8EB6425C750B50040BC40 /* AcornADF.cpp */; }; 4BE90FFD22D5864800FB464D /* MacintoshVideoTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */; }; 4BE9A6B11EDE293000CBCB47 /* zexdoc.com in Resources */ = {isa = PBXBuildFile; fileRef = 4BE9A6B01EDE293000CBCB47 /* zexdoc.com */; }; 4BEA525E1DF33323007E74F2 /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEA525D1DF33323007E74F2 /* Tape.cpp */; }; @@ -1788,6 +1789,8 @@ 4BE34437238389E10058E78F /* AtariSTVideoTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AtariSTVideoTests.mm; sourceTree = ""; }; 4BE76CF822641ED300ACD6FA /* QLTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = QLTests.mm; sourceTree = ""; }; 4BE845201F2FF7F100A5EA22 /* CRTC6845.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CRTC6845.hpp; path = 6845/CRTC6845.hpp; sourceTree = ""; }; + 4BE8EB6425C750B50040BC40 /* AcornADF.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = AcornADF.cpp; sourceTree = ""; }; + 4BE8EB6525C750B50040BC40 /* AcornADF.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = AcornADF.hpp; sourceTree = ""; }; 4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MacintoshVideoTests.mm; sourceTree = ""; }; 4BE9A6B01EDE293000CBCB47 /* zexdoc.com */ = {isa = PBXFileReference; lastKnownFileType = file; name = zexdoc.com; path = Zexall/zexdoc.com; sourceTree = ""; }; 4BEA525D1DF33323007E74F2 /* Tape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Tape.cpp; path = Electron/Tape.cpp; sourceTree = ""; }; @@ -2733,6 +2736,8 @@ children = ( 4B74CF7F2312FA9C00500CE8 /* HFV.hpp */, 4B74CF802312FA9C00500CE8 /* HFV.cpp */, + 4BE8EB6425C750B50040BC40 /* AcornADF.cpp */, + 4BE8EB6525C750B50040BC40 /* AcornADF.hpp */, ); path = Formats; sourceTree = ""; @@ -4770,6 +4775,7 @@ 4B2BFDB21DAEF5FF001A68B8 /* Video.cpp in Sources */, 4BEDA3BF25B25563000C2DBD /* Decoder.cpp in Sources */, 4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */, + 4BE8EB6625C750B50040BC40 /* AcornADF.cpp in Sources */, 4BBFFEE61F7B27F1005F3FEB /* TrackSerialiser.cpp in Sources */, 4BAE49582032881E004BE78E /* CSZX8081.mm in Sources */, 4B0333AF2094081A0050B93D /* AppleDSK.cpp in Sources */, diff --git a/Storage/MassStorage/Formats/AcornADF.cpp b/Storage/MassStorage/Formats/AcornADF.cpp new file mode 100644 index 000000000..f6d393e1f --- /dev/null +++ b/Storage/MassStorage/Formats/AcornADF.cpp @@ -0,0 +1,36 @@ +// +// AcornADF.cpp +// Clock Signal +// +// Created by Thomas Harte on 31/01/2021. +// Copyright © 2021 Thomas Harte. All rights reserved. +// + +#include "AcornADF.hpp" + +using namespace Storage::MassStorage; + +AcornADF::AcornADF(const std::string &file_name) : file_(file_name) { + // Only one sanity check: is the file a multiple of 256 bytes in size? + // [TODO: and larger than a floppy disk?] + const auto file_size = file_.stats().st_size; + if(file_size & 255) throw std::exception(); +} + +size_t AcornADF::get_block_size() { + return 256; +} + +size_t AcornADF::get_number_of_blocks() { + return size_t(file_.stats().st_size) / 256; +} + +std::vector AcornADF::get_block(size_t address) { + file_.seek(long(address * 256), SEEK_SET); + return file_.read(256); +} + +void AcornADF::set_block(size_t address, const std::vector &contents) { + file_.seek(long(address * 256), SEEK_SET); + file_.write(contents); +} diff --git a/Storage/MassStorage/Formats/AcornADF.hpp b/Storage/MassStorage/Formats/AcornADF.hpp new file mode 100644 index 000000000..18eb6e4a7 --- /dev/null +++ b/Storage/MassStorage/Formats/AcornADF.hpp @@ -0,0 +1,39 @@ +// +// AcornADF.hpp +// Clock Signal +// +// Created by Thomas Harte on 31/01/2021. +// Copyright © 2021 Thomas Harte. All rights reserved. +// + +#ifndef AcornADF_hpp +#define AcornADF_hpp + +#include "../MassStorageDevice.hpp" +#include "../../FileHolder.hpp" + +namespace Storage { +namespace MassStorage { + +/*! + Provides a @c MassStorageDevice containing an Acorn ADFS image, which is just a + sector dump of an ADFS volume. +*/ +class AcornADF: public MassStorageDevice { + public: + AcornADF(const std::string &file_name); + + private: + FileHolder file_; + + /* MassStorageDevices overrides. */ + size_t get_block_size() final; + size_t get_number_of_blocks() final; + std::vector get_block(size_t address) final; + void set_block(size_t address, const std::vector &) final; +}; + +} +} + +#endif /* AcornADF_hpp */ From 906b6ccdb7054773e265fc70dc1d32c2d9f99394 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 31 Jan 2021 18:36:29 -0500 Subject: [PATCH 03/11] This appears to be sufficient for the Electron to _read_ SCSI. So that's step one. --- Analyser/Static/Acorn/StaticAnalyser.cpp | 3 + Machines/Electron/Electron.cpp | 109 +++++++++++++++--- Storage/MassStorage/Formats/AcornADF.hpp | 6 +- Storage/MassStorage/SCSI/SCSI.hpp | 7 ++ .../MassStorage/SCSI/TargetImplementation.hpp | 4 +- 5 files changed, 108 insertions(+), 21 deletions(-) diff --git a/Analyser/Static/Acorn/StaticAnalyser.cpp b/Analyser/Static/Acorn/StaticAnalyser.cpp index e37ffc7c8..3722a3815 100644 --- a/Analyser/Static/Acorn/StaticAnalyser.cpp +++ b/Analyser/Static/Acorn/StaticAnalyser.cpp @@ -146,6 +146,9 @@ Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &me if(!media.mass_storage_devices.empty()) { target->has_pres_adfs = false; target->has_acorn_adfs = true; + + // TODO: validate an ADFS catalogue, at least. + target->media.mass_storage_devices = media.mass_storage_devices; } TargetList targets; diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index ad148b88f..a885203b8 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -17,6 +17,9 @@ #include "../../Configurable/StandardOptions.hpp" #include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp" #include "../../Processors/6502/6502.hpp" + +#include "../../Storage/MassStorage/SCSI/SCSI.hpp" +#include "../../Storage/MassStorage/SCSI/DirectAccessDevice.hpp" #include "../../Storage/Tape/Tape.hpp" #include "../Utility/Typer.hpp" @@ -31,7 +34,7 @@ namespace Electron { -class ConcreteMachine: +template class ConcreteMachine: public Machine, public MachineTypes::TimedMachine, public MachineTypes::ScanProducer, @@ -46,6 +49,9 @@ class ConcreteMachine: public: ConcreteMachine(const Analyser::Static::Acorn::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : m6502_(*this), + scsi_bus_(4'000'000), + hard_drive_(scsi_bus_, 0), + scsi_device_(scsi_bus_.add_device()), video_output_(ram_), sound_generator_(audio_queue_), speaker_(sound_generator_) { @@ -202,7 +208,12 @@ class ConcreteMachine: set_rom(slot, cartridge->get_segments().front().data, false); } - return !media.tapes.empty() || !media.disks.empty() || !media.cartridges.empty(); + // TODO: allow this only at machine startup? + if(!media.mass_storage_devices.empty()) { + hard_drive_->set_storage(media.mass_storage_devices.front()); + } + + return !media.empty(); } forceinline Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) { @@ -307,7 +318,6 @@ class ConcreteMachine: break; case 0xfc04: case 0xfc05: case 0xfc06: case 0xfc07: - printf("%04x %s %02x\n", address, isReadOperation(operation) ? "->" : "<-", *value); if(plus3_ && (address&0x00f0) == 0x00c0) { if(is_holding_shift_ && address == 0xfcc4) { is_holding_shift_ = false; @@ -320,15 +330,62 @@ class ConcreteMachine: } break; case 0xfc00: - printf("%04x %s %02x\n", address, isReadOperation(operation) ? "->" : "<-", *value); if(plus3_ && (address&0x00f0) == 0x00c0) { if(!isReadOperation(operation)) { plus3_->set_control_register(*value); } else *value = 1; } + + if(has_scsi_bus && (address&0x00f0) == 0x0040) { + scsi_acknowledge_ = true; + if(!isReadOperation(operation)) { + scsi_data_ = *value; + push_scsi_output(); + } else { + *value = SCSI::data_lines(scsi_bus_.get_state()); + push_scsi_output(); + } + } break; case 0xfc03: - printf("%04x %s %02x\n", address, isReadOperation(operation) ? "->" : "<-", *value); + if(has_scsi_bus && (address&0x00f0) == 0x0040) { + printf("SCSI IRQ: %s %02x\n", isReadOperation(operation) ? "->" : "<-", *value); + } + break; + case 0xfc01: + if(has_scsi_bus && (address&0x00f0) == 0x0040 && isReadOperation(operation)) { + // Status byte is: + // + // b7: SCSI C/D + // b6: SCSI I/O + // b5: SCSI REQ + // b4: interrupt flag + // b3: 0 + // b2: 0 + // b1: SCSI BSY + // b0: SCSI MSG + const auto state = scsi_bus_.get_state(); + *value = + (state & SCSI::Line::Control ? 0x80 : 0x00) | + (state & SCSI::Line::Input ? 0x40 : 0x00) | + (state & SCSI::Line::Request ? 0x20 : 0x00) | + (state & SCSI::Line::Busy ? 0x02 : 0x00) | + (state & SCSI::Line::Message ? 0x01 : 0x00); + // TODO: interrupt flag. + + // Empirical guess: this is also the trigger to affect busy/request/acknowledge + // signalling. Maybe? + if(scsi_select_ && scsi_bus_.get_state() & SCSI::Line::Busy) { + scsi_select_ = false; + push_scsi_output(); + } + } + break; + case 0xfc02: + if(has_scsi_bus && (address&0x00f0) == 0x0040) { + scsi_select_ = true; + push_scsi_output(); + } break; // SCSI locations: @@ -338,16 +395,6 @@ class ConcreteMachine: // fc42: select write // fc43: interrupt latch // - // Status byte is: - // - // b7: SCSI C/D - // b6: SCSI I/O - // b5: SCSI REQ - // b4: interrupt flag - // b3: 0 - // b2: 0 - // b1: SCSI BSY - // b0: SCSI MSG // // Interrupt latch is: // @@ -451,6 +498,16 @@ class ConcreteMachine: } } + // TODO: clock/change observe. + if(has_scsi_bus) { + scsi_bus_.run_for(Cycles(int(cycles))); + + if(scsi_acknowledge_ && !(scsi_bus_.get_state() & SCSI::Line::Request)) { + scsi_acknowledge_ = false; + push_scsi_output(); + } + } + return Cycles(int(cycles)); } @@ -681,6 +738,21 @@ class ConcreteMachine: bool is_holding_shift_ = false; int shift_restart_counter_ = 0; + // Hard drive. + SCSI::Bus scsi_bus_; + SCSI::Target::Target hard_drive_; + const size_t scsi_device_ = 0; + uint8_t scsi_data_ = 0; + bool scsi_select_ = false; + bool scsi_acknowledge_ = false; + void push_scsi_output() { + scsi_bus_.set_device_output(scsi_device_, + scsi_data_ | + (scsi_select_ ? SCSI::Line::SelectTarget : 0) | + (scsi_acknowledge_ ? SCSI::Line::Acknowledge : 0) + ); + } + // Outputs VideoOutput video_output_; @@ -703,7 +775,12 @@ using namespace Electron; Machine *Machine::Electron(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) { using Target = Analyser::Static::Acorn::Target; const Target *const acorn_target = dynamic_cast(target); - return new Electron::ConcreteMachine(*acorn_target, rom_fetcher); + + if(acorn_target->media.mass_storage_devices.empty()) { + return new Electron::ConcreteMachine(*acorn_target, rom_fetcher); + } else { + return new Electron::ConcreteMachine(*acorn_target, rom_fetcher); + } } Machine::~Machine() {} diff --git a/Storage/MassStorage/Formats/AcornADF.hpp b/Storage/MassStorage/Formats/AcornADF.hpp index 18eb6e4a7..2c979e1dd 100644 --- a/Storage/MassStorage/Formats/AcornADF.hpp +++ b/Storage/MassStorage/Formats/AcornADF.hpp @@ -6,8 +6,8 @@ // Copyright © 2021 Thomas Harte. All rights reserved. // -#ifndef AcornADF_hpp -#define AcornADF_hpp +#ifndef MassStorage_AcornADF_hpp +#define MassStorage_AcornADF_hpp #include "../MassStorageDevice.hpp" #include "../../FileHolder.hpp" @@ -36,4 +36,4 @@ class AcornADF: public MassStorageDevice { } } -#endif /* AcornADF_hpp */ +#endif /* MassStorage_AcornADF_hpp */ diff --git a/Storage/MassStorage/SCSI/SCSI.hpp b/Storage/MassStorage/SCSI/SCSI.hpp index 538f1f30f..c896f7c3b 100644 --- a/Storage/MassStorage/SCSI/SCSI.hpp +++ b/Storage/MassStorage/SCSI/SCSI.hpp @@ -90,6 +90,13 @@ constexpr double DeskewDelay = ns(45.0); /// any two devices. constexpr double CableSkew = ns(10.0); +/*! + @returns The value of the data lines per @c state. +*/ +constexpr uint8_t data_lines(BusState state) { + return uint8_t(state & 0xff); +} + #undef ns #undef us diff --git a/Storage/MassStorage/SCSI/TargetImplementation.hpp b/Storage/MassStorage/SCSI/TargetImplementation.hpp index ff6ccb297..5a4596d1a 100644 --- a/Storage/MassStorage/SCSI/TargetImplementation.hpp +++ b/Storage/MassStorage/SCSI/TargetImplementation.hpp @@ -17,8 +17,8 @@ template void Target::scsi_bus_did_change(Bus *, B /* "The target determines that it is selected when the SEL# signal and its SCSI ID bit are active and the BSY# and I#/O signals - are false. It then asserts the signal within a selection abort - time." + are false. It then asserts the signal within a selection + abort time." */ // Wait for deskew, at the very least. From 07df7572b33cc8105e2bfd22632225f31e6dd674 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 31 Jan 2021 21:03:09 -0500 Subject: [PATCH 04/11] Switch to preferred Acorn-world extension: DAT. --- Analyser/Static/Acorn/StaticAnalyser.cpp | 10 ++++- Analyser/Static/StaticAnalyser.cpp | 2 + .../Clock Signal.xcodeproj/project.pbxproj | 12 +++--- OSBindings/Mac/Clock Signal/Info.plist | 20 +++++++++ Storage/MassStorage/Formats/AcornADF.cpp | 36 ---------------- Storage/MassStorage/Formats/DAT.cpp | 41 +++++++++++++++++++ .../Formats/{AcornADF.hpp => DAT.hpp} | 15 +++---- 7 files changed, 85 insertions(+), 51 deletions(-) delete mode 100644 Storage/MassStorage/Formats/AcornADF.cpp create mode 100644 Storage/MassStorage/Formats/DAT.cpp rename Storage/MassStorage/Formats/{AcornADF.hpp => DAT.hpp} (68%) diff --git a/Analyser/Static/Acorn/StaticAnalyser.cpp b/Analyser/Static/Acorn/StaticAnalyser.cpp index 3722a3815..dcf564567 100644 --- a/Analyser/Static/Acorn/StaticAnalyser.cpp +++ b/Analyser/Static/Acorn/StaticAnalyser.cpp @@ -146,9 +146,15 @@ Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &me if(!media.mass_storage_devices.empty()) { target->has_pres_adfs = false; target->has_acorn_adfs = true; - - // TODO: validate an ADFS catalogue, at least. target->media.mass_storage_devices = media.mass_storage_devices; + + // Check for a boot option. + const auto sector = target->media.mass_storage_devices.front()->get_block(1); + if(sector[0xfd]) { + target->should_shift_restart = true; + } else { + target->loading_command = "*CAT\n"; + } } TargetList targets; diff --git a/Analyser/Static/StaticAnalyser.cpp b/Analyser/Static/StaticAnalyser.cpp index ad80cd8fb..57fc02a45 100644 --- a/Analyser/Static/StaticAnalyser.cpp +++ b/Analyser/Static/StaticAnalyser.cpp @@ -51,6 +51,7 @@ #include "../../Storage/Disk/DiskImage/Formats/WOZ.hpp" // Mass Storage Devices (i.e. usually, hard disks) +#include "../../Storage/MassStorage/Formats/DAT.hpp" #include "../../Storage/MassStorage/Formats/HFV.hpp" // Tapes @@ -105,6 +106,7 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform:: Format("col", result.cartridges, Cartridge::BinaryDump, TargetPlatform::ColecoVision) // COL Format("csw", result.tapes, Tape::CSW, TargetPlatform::AllTape) // CSW Format("d64", result.disks, Disk::DiskImageHolder, TargetPlatform::Commodore) // D64 + Format("dat", result.mass_storage_devices, MassStorage::DAT, TargetPlatform::Acorn) // DAT Format("dmk", result.disks, Disk::DiskImageHolder, TargetPlatform::MSX) // DMK Format("do", result.disks, Disk::DiskImageHolder, TargetPlatform::DiskII) // DO Format("dsd", result.disks, Disk::DiskImageHolder, TargetPlatform::Acorn) // DSD diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 761c00ae3..d33f3f630 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -850,7 +850,7 @@ 4BE211DE253E4E4800435408 /* 65C02_no_Rockwell_test.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4BE211DD253E4E4800435408 /* 65C02_no_Rockwell_test.bin */; }; 4BE34438238389E10058E78F /* AtariSTVideoTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE34437238389E10058E78F /* AtariSTVideoTests.mm */; }; 4BE76CF922641ED400ACD6FA /* QLTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE76CF822641ED300ACD6FA /* QLTests.mm */; }; - 4BE8EB6625C750B50040BC40 /* AcornADF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE8EB6425C750B50040BC40 /* AcornADF.cpp */; }; + 4BE8EB6625C750B50040BC40 /* DAT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE8EB6425C750B50040BC40 /* DAT.cpp */; }; 4BE90FFD22D5864800FB464D /* MacintoshVideoTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */; }; 4BE9A6B11EDE293000CBCB47 /* zexdoc.com in Resources */ = {isa = PBXBuildFile; fileRef = 4BE9A6B01EDE293000CBCB47 /* zexdoc.com */; }; 4BEA525E1DF33323007E74F2 /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEA525D1DF33323007E74F2 /* Tape.cpp */; }; @@ -1789,8 +1789,8 @@ 4BE34437238389E10058E78F /* AtariSTVideoTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AtariSTVideoTests.mm; sourceTree = ""; }; 4BE76CF822641ED300ACD6FA /* QLTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = QLTests.mm; sourceTree = ""; }; 4BE845201F2FF7F100A5EA22 /* CRTC6845.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CRTC6845.hpp; path = 6845/CRTC6845.hpp; sourceTree = ""; }; - 4BE8EB6425C750B50040BC40 /* AcornADF.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = AcornADF.cpp; sourceTree = ""; }; - 4BE8EB6525C750B50040BC40 /* AcornADF.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = AcornADF.hpp; sourceTree = ""; }; + 4BE8EB6425C750B50040BC40 /* DAT.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DAT.cpp; sourceTree = ""; }; + 4BE8EB6525C750B50040BC40 /* DAT.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DAT.hpp; sourceTree = ""; }; 4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MacintoshVideoTests.mm; sourceTree = ""; }; 4BE9A6B01EDE293000CBCB47 /* zexdoc.com */ = {isa = PBXFileReference; lastKnownFileType = file; name = zexdoc.com; path = Zexall/zexdoc.com; sourceTree = ""; }; 4BEA525D1DF33323007E74F2 /* Tape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Tape.cpp; path = Electron/Tape.cpp; sourceTree = ""; }; @@ -2736,8 +2736,8 @@ children = ( 4B74CF7F2312FA9C00500CE8 /* HFV.hpp */, 4B74CF802312FA9C00500CE8 /* HFV.cpp */, - 4BE8EB6425C750B50040BC40 /* AcornADF.cpp */, - 4BE8EB6525C750B50040BC40 /* AcornADF.hpp */, + 4BE8EB6425C750B50040BC40 /* DAT.cpp */, + 4BE8EB6525C750B50040BC40 /* DAT.hpp */, ); path = Formats; sourceTree = ""; @@ -4775,7 +4775,7 @@ 4B2BFDB21DAEF5FF001A68B8 /* Video.cpp in Sources */, 4BEDA3BF25B25563000C2DBD /* Decoder.cpp in Sources */, 4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */, - 4BE8EB6625C750B50040BC40 /* AcornADF.cpp in Sources */, + 4BE8EB6625C750B50040BC40 /* DAT.cpp in Sources */, 4BBFFEE61F7B27F1005F3FEB /* TrackSerialiser.cpp in Sources */, 4BAE49582032881E004BE78E /* CSZX8081.mm in Sources */, 4B0333AF2094081A0050B93D /* AppleDSK.cpp in Sources */, diff --git a/OSBindings/Mac/Clock Signal/Info.plist b/OSBindings/Mac/Clock Signal/Info.plist index 7f07c53ec..83809e66f 100644 --- a/OSBindings/Mac/Clock Signal/Info.plist +++ b/OSBindings/Mac/Clock Signal/Info.plist @@ -552,6 +552,26 @@ NSDocumentClass $(PRODUCT_MODULE_NAME).MachineDocument + + CFBundleTypeExtensions + + dat + + CFBundleTypeName + Electron/BBC Hard Disk Image + CFBundleTypeOSTypes + + ???? + + CFBundleTypeRole + Editor + LSHandlerRank + Owner + LSTypeIsPackage + + NSDocumentClass + $(PRODUCT_MODULE_NAME).MachineDocument + CFBundleExecutable $(EXECUTABLE_NAME) diff --git a/Storage/MassStorage/Formats/AcornADF.cpp b/Storage/MassStorage/Formats/AcornADF.cpp deleted file mode 100644 index f6d393e1f..000000000 --- a/Storage/MassStorage/Formats/AcornADF.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// -// AcornADF.cpp -// Clock Signal -// -// Created by Thomas Harte on 31/01/2021. -// Copyright © 2021 Thomas Harte. All rights reserved. -// - -#include "AcornADF.hpp" - -using namespace Storage::MassStorage; - -AcornADF::AcornADF(const std::string &file_name) : file_(file_name) { - // Only one sanity check: is the file a multiple of 256 bytes in size? - // [TODO: and larger than a floppy disk?] - const auto file_size = file_.stats().st_size; - if(file_size & 255) throw std::exception(); -} - -size_t AcornADF::get_block_size() { - return 256; -} - -size_t AcornADF::get_number_of_blocks() { - return size_t(file_.stats().st_size) / 256; -} - -std::vector AcornADF::get_block(size_t address) { - file_.seek(long(address * 256), SEEK_SET); - return file_.read(256); -} - -void AcornADF::set_block(size_t address, const std::vector &contents) { - file_.seek(long(address * 256), SEEK_SET); - file_.write(contents); -} diff --git a/Storage/MassStorage/Formats/DAT.cpp b/Storage/MassStorage/Formats/DAT.cpp new file mode 100644 index 000000000..ceb00c7ab --- /dev/null +++ b/Storage/MassStorage/Formats/DAT.cpp @@ -0,0 +1,41 @@ +// +// DAT.cpp +// Clock Signal +// +// Created by Thomas Harte on 31/01/2021. +// Copyright © 2021 Thomas Harte. All rights reserved. +// + +#include "DAT.hpp" + +using namespace Storage::MassStorage; + +DAT::DAT(const std::string &file_name) : file_(file_name) { + // Is the file a multiple of 256 bytes in size? + const auto file_size = file_.stats().st_size; + if(file_size & 255) throw std::exception(); + + // Does it contain the 'Hugo' signature? + file_.seek(0x201, SEEK_SET); + if(!file_.check_signature("Hugo")) { + throw std::exception(); + } +} + +size_t DAT::get_block_size() { + return 256; +} + +size_t DAT::get_number_of_blocks() { + return size_t(file_.stats().st_size) / 256; +} + +std::vector DAT::get_block(size_t address) { + file_.seek(long(address * 256), SEEK_SET); + return file_.read(256); +} + +void DAT::set_block(size_t address, const std::vector &contents) { + file_.seek(long(address * 256), SEEK_SET); + file_.write(contents); +} diff --git a/Storage/MassStorage/Formats/AcornADF.hpp b/Storage/MassStorage/Formats/DAT.hpp similarity index 68% rename from Storage/MassStorage/Formats/AcornADF.hpp rename to Storage/MassStorage/Formats/DAT.hpp index 2c979e1dd..7ec0350e4 100644 --- a/Storage/MassStorage/Formats/AcornADF.hpp +++ b/Storage/MassStorage/Formats/DAT.hpp @@ -1,13 +1,13 @@ // -// AcornADF.hpp +// DAT.hpp // Clock Signal // // Created by Thomas Harte on 31/01/2021. // Copyright © 2021 Thomas Harte. All rights reserved. // -#ifndef MassStorage_AcornADF_hpp -#define MassStorage_AcornADF_hpp +#ifndef MassStorage_DAT_hpp +#define MassStorage_DAT_hpp #include "../MassStorageDevice.hpp" #include "../../FileHolder.hpp" @@ -17,11 +17,12 @@ namespace MassStorage { /*! Provides a @c MassStorageDevice containing an Acorn ADFS image, which is just a - sector dump of an ADFS volume. + sector dump of an ADFS volume. It will be validated for an ADFS catalogue and communicate + in 256-byte blocks. */ -class AcornADF: public MassStorageDevice { +class DAT: public MassStorageDevice { public: - AcornADF(const std::string &file_name); + DAT(const std::string &file_name); private: FileHolder file_; @@ -36,4 +37,4 @@ class AcornADF: public MassStorageDevice { } } -#endif /* MassStorage_AcornADF_hpp */ +#endif /* MassStorage_DAT_hpp */ From 274b3c7d245880ad8c80fb89f66f1f51209d3c4c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 31 Jan 2021 21:24:54 -0500 Subject: [PATCH 05/11] Handles SCSI changes on-demand. --- Machines/Electron/Electron.cpp | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index a885203b8..7c6670564 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -45,7 +45,9 @@ template class ConcreteMachine: public CPU::MOS6502::BusHandler, public Tape::Delegate, public Utility::TypeRecipient, - public Activity::Source { + public Activity::Source, + public SCSI::Bus::Observer, + public ClockingHint::Observer { public: ConcreteMachine(const Analyser::Static::Acorn::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : m6502_(*this), @@ -143,6 +145,11 @@ template class ConcreteMachine: if(target.should_shift_restart) { shift_restart_counter_ = 1000000; } + + if(has_scsi_bus) { + scsi_bus_.add_observer(this); + scsi_bus_.set_clocking_hint_observer(this); + } } ~ConcreteMachine() { @@ -498,13 +505,9 @@ template class ConcreteMachine: } } - // TODO: clock/change observe. - if(has_scsi_bus) { - scsi_bus_.run_for(Cycles(int(cycles))); - - if(scsi_acknowledge_ && !(scsi_bus_.get_state() & SCSI::Line::Request)) { - scsi_acknowledge_ = false; - push_scsi_output(); + if constexpr (has_scsi_bus) { + if(scsi_is_clocked_) { + scsi_bus_.run_for(Cycles(int(cycles))); } } @@ -541,6 +544,18 @@ template class ConcreteMachine: m6502_.run_for(cycles); } + void scsi_bus_did_change(SCSI::Bus *, SCSI::BusState new_state, double) final { + // Release acknowledge when request is released. + if(scsi_acknowledge_ && !(new_state & SCSI::Line::Request)) { + scsi_acknowledge_ = false; + push_scsi_output(); + } + } + + void set_component_prefers_clocking(ClockingHint::Source *, ClockingHint::Preference preference) final { + scsi_is_clocked_ = preference != ClockingHint::Preference::None; + } + void tape_did_change_interrupt_status(Tape *) final { interrupt_status_ = (interrupt_status_ & ~(Interrupt::TransmitDataEmpty | Interrupt::ReceiveDataFull | Interrupt::HighToneDetect)) | tape_.get_interrupt_status(); evaluate_interrupts(); @@ -745,6 +760,7 @@ template class ConcreteMachine: uint8_t scsi_data_ = 0; bool scsi_select_ = false; bool scsi_acknowledge_ = false; + bool scsi_is_clocked_ = false; void push_scsi_output() { scsi_bus_.set_device_output(scsi_device_, scsi_data_ | From 53514c7fdc4b430bcbb7897cfa8ac66f7ab6eed2 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 31 Jan 2021 21:28:55 -0500 Subject: [PATCH 06/11] Ensures non-breakage of Qt interface. --- Machines/Electron/Electron.cpp | 2 +- OSBindings/Qt/mainwindow.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 7c6670564..65faa203d 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -356,7 +356,7 @@ template class ConcreteMachine: break; case 0xfc03: if(has_scsi_bus && (address&0x00f0) == 0x0040) { - printf("SCSI IRQ: %s %02x\n", isReadOperation(operation) ? "->" : "<-", *value); + // TODO: SCSI IRQ. Once I'm clear on the use. } break; case 0xfc01: diff --git a/OSBindings/Qt/mainwindow.cpp b/OSBindings/Qt/mainwindow.cpp index 8a0b781da..86afb4691 100644 --- a/OSBindings/Qt/mainwindow.cpp +++ b/OSBindings/Qt/mainwindow.cpp @@ -1163,7 +1163,7 @@ void MainWindow::start_electron() { auto target = std::make_unique(); target->has_dfs = ui->electronDFSCheckBox->isChecked(); - target->has_adfs = ui->electronADFSCheckBox->isChecked(); + target->has_pres_adfs = ui->electronADFSCheckBox->isChecked(); target->has_ap6_rom = ui->electronAP6CheckBox->isChecked(); target->has_sideways_ram = ui->electronSidewaysRAMCheckBox->isChecked(); From 1a40cc048e1dbda364e8d906a0297063303fb4aa Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 31 Jan 2021 21:41:11 -0500 Subject: [PATCH 07/11] Niceties: include AP6 ROM for hard-disk users; show SCSI activity indicator. --- Analyser/Static/Acorn/StaticAnalyser.cpp | 8 +++++++- Machines/Electron/Electron.cpp | 10 +++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Analyser/Static/Acorn/StaticAnalyser.cpp b/Analyser/Static/Acorn/StaticAnalyser.cpp index dcf564567..d5a34167b 100644 --- a/Analyser/Static/Acorn/StaticAnalyser.cpp +++ b/Analyser/Static/Acorn/StaticAnalyser.cpp @@ -144,8 +144,14 @@ Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &me // Enable the Acorn ADFS if a mass-storage device is attached; // unlike the Pres ADFS it retains SCSI logic. if(!media.mass_storage_devices.empty()) { - target->has_pres_adfs = false; + target->has_pres_adfs = false; // To override a floppy selection, if one was made. target->has_acorn_adfs = true; + + // Assume some sort of later-era Acorn work is likely to happen; + // so ensure *TYPE, etc are present. + target->has_ap6_rom = true; + target->has_sideways_ram = true; + target->media.mass_storage_devices = media.mass_storage_devices; // Check for a boot option. diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 65faa203d..2af9cb575 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -612,10 +612,14 @@ template class ConcreteMachine: if(activity_observer_) { activity_observer_->register_led(caps_led); activity_observer_->set_led_status(caps_led, caps_led_state_); + } - if(plus3_) { - plus3_->set_activity_observer(observer); - } + if(plus3_) { + plus3_->set_activity_observer(observer); + } + + if(has_scsi_bus) { + scsi_bus_.set_activity_observer(observer); } } From 9f202d423839ca01e9609fcf255ad4bbe4a3d6d5 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 1 Feb 2021 17:40:11 -0500 Subject: [PATCH 08/11] Adds SCSI interrupt support. --- Machines/Electron/Electron.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 2af9cb575..f5f71a8c9 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -356,7 +356,9 @@ template class ConcreteMachine: break; case 0xfc03: if(has_scsi_bus && (address&0x00f0) == 0x0040) { - // TODO: SCSI IRQ. Once I'm clear on the use. + scsi_interrupt_state_ = false; + scsi_interrupt_mask_ = *value & 1; + evaluate_interrupts(); } break; case 0xfc01: @@ -376,9 +378,9 @@ template class ConcreteMachine: (state & SCSI::Line::Control ? 0x80 : 0x00) | (state & SCSI::Line::Input ? 0x40 : 0x00) | (state & SCSI::Line::Request ? 0x20 : 0x00) | + ((scsi_interrupt_state_ && scsi_interrupt_mask_) ? 0x10 : 0x00) | (state & SCSI::Line::Busy ? 0x02 : 0x00) | (state & SCSI::Line::Message ? 0x01 : 0x00); - // TODO: interrupt flag. // Empirical guess: this is also the trigger to affect busy/request/acknowledge // signalling. Maybe? @@ -550,6 +552,10 @@ template class ConcreteMachine: scsi_acknowledge_ = false; push_scsi_output(); } + + scsi_interrupt_state_ |= (new_state^previous_bus_state_)&new_state & SCSI::Line::Request; + previous_bus_state_ = new_state; + evaluate_interrupts(); } void set_component_prefers_clocking(ClockingHint::Source *, ClockingHint::Preference preference) final { @@ -713,7 +719,12 @@ template class ConcreteMachine: } else { interrupt_status_ &= ~1; } - m6502_.set_irq_line(interrupt_status_ & 1); + + if constexpr (has_scsi_bus) { + m6502_.set_irq_line((scsi_interrupt_state_ && scsi_interrupt_mask_) | (interrupt_status_ & 1)); + } else { + m6502_.set_irq_line(interrupt_status_ & 1); + } } CPU::MOS6502::Processor m6502_; @@ -760,11 +771,14 @@ template class ConcreteMachine: // Hard drive. SCSI::Bus scsi_bus_; SCSI::Target::Target hard_drive_; + SCSI::BusState previous_bus_state_ = SCSI::DefaultBusState; const size_t scsi_device_ = 0; uint8_t scsi_data_ = 0; bool scsi_select_ = false; bool scsi_acknowledge_ = false; bool scsi_is_clocked_ = false; + bool scsi_interrupt_state_ = false; + bool scsi_interrupt_mask_ = false; void push_scsi_output() { scsi_bus_.set_device_output(scsi_device_, scsi_data_ | From 2a8e8a49826a4eacffe836dc90aa66aea75e2efe Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 2 Feb 2021 20:24:19 -0500 Subject: [PATCH 09/11] Slightly increases logging. --- Storage/MassStorage/SCSI/DirectAccessDevice.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Storage/MassStorage/SCSI/DirectAccessDevice.cpp b/Storage/MassStorage/SCSI/DirectAccessDevice.cpp index 2fc6039b7..f6944d2f0 100644 --- a/Storage/MassStorage/SCSI/DirectAccessDevice.cpp +++ b/Storage/MassStorage/SCSI/DirectAccessDevice.cpp @@ -38,6 +38,7 @@ bool DirectAccessDevice::write(const Target::CommandState &state, Target::Respon if(!device_) return false; const auto specs = state.read_write_specs(); + LOG("Write: " << specs.number_of_blocks << " to " << specs.address); responder.receive_data(device_->get_block_size() * specs.number_of_blocks, [this, specs] (const Target::CommandState &state, Target::Responder &responder) { const auto received_data = state.received_data(); From f57e8970855b81914093e302537cd100bb3725d4 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 2 Feb 2021 20:24:39 -0500 Subject: [PATCH 10/11] Corrects visibility of SCSI output. --- Machines/Electron/Electron.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index f5f71a8c9..076547ff1 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -553,6 +553,12 @@ template class ConcreteMachine: push_scsi_output(); } + // Output occurs only while SCSI::Line::Input is inactive; therefore a change + // in that line affects what's on the bus. + if(((new_state^previous_bus_state_)&SCSI::Line::Input)) { + push_scsi_output(); + } + scsi_interrupt_state_ |= (new_state^previous_bus_state_)&new_state & SCSI::Line::Request; previous_bus_state_ = new_state; evaluate_interrupts(); @@ -781,7 +787,7 @@ template class ConcreteMachine: bool scsi_interrupt_mask_ = false; void push_scsi_output() { scsi_bus_.set_device_output(scsi_device_, - scsi_data_ | + (scsi_bus_.get_state()&SCSI::Line::Input ? 0 : scsi_data_) | (scsi_select_ ? SCSI::Line::SelectTarget : 0) | (scsi_acknowledge_ ? SCSI::Line::Acknowledge : 0) ); From beb514b231b912a4a44da9fc5145125db798b555 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 2 Feb 2021 20:37:15 -0500 Subject: [PATCH 11/11] Adds an additional mapping for copy. --- Machines/Electron/Keyboard.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Machines/Electron/Keyboard.cpp b/Machines/Electron/Keyboard.cpp index 887ac5aff..c554c2cbe 100644 --- a/Machines/Electron/Keyboard.cpp +++ b/Machines/Electron/Keyboard.cpp @@ -16,6 +16,7 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) const { default: break; BIND(BackTick, KeyCopy); + BIND(Backslash, KeyCopy); BIND(k0, Key0); BIND(k1, Key1); BIND(k2, Key2); BIND(k3, Key3); BIND(k4, Key4); BIND(k5, Key5); BIND(k6, Key6); BIND(k7, Key7); BIND(k8, Key8); BIND(k9, Key9);