From bb2cf0170d7492889750fff263db10179a922ac4 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 24 Apr 2025 20:49:54 -0400 Subject: [PATCH 01/48] Start trying to draft an EXOS device ROM. --- Machines/Enterprise/HostFS.hpp | 23 +++++++++++++++++++ .../Clock Signal.xcodeproj/project.pbxproj | 2 ++ 2 files changed, 25 insertions(+) create mode 100644 Machines/Enterprise/HostFS.hpp diff --git a/Machines/Enterprise/HostFS.hpp b/Machines/Enterprise/HostFS.hpp new file mode 100644 index 000000000..3bee0d117 --- /dev/null +++ b/Machines/Enterprise/HostFS.hpp @@ -0,0 +1,23 @@ +// +// HostFS.h +// Clock Signal +// +// Created by Thomas Harte on 23/04/2025. +// Copyright © 2025 Thomas Harte. All rights reserved. +// + +#pragma once + +namespace Enterprise { + +constexpr uint8_t hostfs_rom[] = { + // Standard header. + 'E', 'X', 'O', 'S', '_', 'R', 'O', 'M', + + // Pointer to device chain. + 0x00, 0x00, + + // +}; + +} diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 87a542254..c5bbcb7ff 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1519,6 +1519,7 @@ 4B322E021F5A29D5004EB04C /* Z80Storage.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Z80Storage.hpp; sourceTree = ""; }; 4B322E031F5A2E3C004EB04C /* Z80Base.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Z80Base.cpp; sourceTree = ""; }; 4B322E051F5A30F5004EB04C /* Z80Implementation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Z80Implementation.hpp; sourceTree = ""; }; + 4B3583DB2DB965FA00A24128 /* HostFS.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = HostFS.hpp; sourceTree = ""; }; 4B37EE801D7345A6006A09A4 /* BinaryDump.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BinaryDump.cpp; sourceTree = ""; }; 4B37EE811D7345A6006A09A4 /* BinaryDump.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = BinaryDump.hpp; sourceTree = ""; }; 4B38F3461F2EC11D00D9235D /* AmstradCPC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AmstradCPC.cpp; sourceTree = ""; }; @@ -2647,6 +2648,7 @@ 4BFEA2EE2682A7B900EBF94C /* Dave.hpp */, 4B051CA02676F52200CA44E8 /* Enterprise.hpp */, 4B051CB52680158600CA44E8 /* EXDos.hpp */, + 4B3583DB2DB965FA00A24128 /* HostFS.hpp */, 4B051CAF267C1CA200CA44E8 /* Keyboard.hpp */, 4B051CAB26783E2000CA44E8 /* Nick.hpp */, ); From d628f7524488e7ce3feb8aeb67b7625f1c35500d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 27 Apr 2025 14:24:11 -0400 Subject: [PATCH 02/48] Switch to using an assembler. --- Machines/Enterprise/HostFS/hostfs.z80s | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 Machines/Enterprise/HostFS/hostfs.z80s diff --git a/Machines/Enterprise/HostFS/hostfs.z80s b/Machines/Enterprise/HostFS/hostfs.z80s new file mode 100644 index 000000000..5ed1fc38b --- /dev/null +++ b/Machines/Enterprise/HostFS/hostfs.z80s @@ -0,0 +1,12 @@ +; +; Designed for assembly with pyz80, https://github.com/simonowen/pyz80/ +; E.g. pyz80 --obj=hostfs.rom hostfs.z80s +; + + dm "EXOS_ROM" ; Standard ROM signature. + dw device_chain ; Pointer to the included device chain. + + ; Startup entry point; there is no startup code. + dw 0 + +device_chain: \ No newline at end of file From 1ca261986e19b8622a46b82744ea11f48cf895bd Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 19 Nov 2025 17:04:39 -0500 Subject: [PATCH 03/48] New direction: attempt a first sweep with the EP128Emu ROM. --- Analyser/Static/StaticAnalyser.hpp | 10 +++++- Machines/Utility/ROMCatalogue.cpp | 9 ++++++ Machines/Utility/ROMCatalogue.hpp | 2 ++ .../Clock Signal.xcodeproj/project.pbxproj | 18 +++++++++++ Storage/FileBundle/FileBundle.cpp | 9 ++++++ Storage/FileBundle/FileBundle.hpp | 31 +++++++++++++++++++ 6 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 Storage/FileBundle/FileBundle.cpp create mode 100644 Storage/FileBundle/FileBundle.hpp diff --git a/Analyser/Static/StaticAnalyser.hpp b/Analyser/Static/StaticAnalyser.hpp index 640c298f1..f3cd2d5a5 100644 --- a/Analyser/Static/StaticAnalyser.hpp +++ b/Analyser/Static/StaticAnalyser.hpp @@ -12,6 +12,7 @@ #include "Storage/Cartridge/Cartridge.hpp" #include "Storage/Disk/Disk.hpp" +#include "Storage/FileBundle/FileBundle.hpp" #include "Storage/MassStorage/MassStorageDevice.hpp" #include "Storage/Tape/Tape.hpp" #include "Storage/TargetPlatforms.hpp" @@ -33,9 +34,15 @@ struct Media { std::vector> tapes; std::vector> cartridges; std::vector> mass_storage_devices; + std::vector> file_bundles; bool empty() const { - return disks.empty() && tapes.empty() && cartridges.empty() && mass_storage_devices.empty(); + return + disks.empty() && + tapes.empty() && + cartridges.empty() && + mass_storage_devices.empty() && + file_bundles.empty(); } Media &operator +=(const Media &rhs) { @@ -47,6 +54,7 @@ struct Media { append(tapes, rhs.tapes); append(cartridges, rhs.cartridges); append(mass_storage_devices, rhs.mass_storage_devices); + append(file_bundles, rhs.file_bundles); return *this; } diff --git a/Machines/Utility/ROMCatalogue.cpp b/Machines/Utility/ROMCatalogue.cpp index d5b2355fb..99a590998 100644 --- a/Machines/Utility/ROMCatalogue.cpp +++ b/Machines/Utility/ROMCatalogue.cpp @@ -670,6 +670,15 @@ const std::vector &Description::all_roms() { 0xe6daa0e9u }, + { + EnterpriseEPFILEIO, + "Enterprise", + "the EP128Emu direction FILE IO ROM", + "epfileio.rom", + 16_kb, + 0x60c79925u + }, + // // Macintosh // diff --git a/Machines/Utility/ROMCatalogue.hpp b/Machines/Utility/ROMCatalogue.hpp index 3d46e70cd..88b621db4 100644 --- a/Machines/Utility/ROMCatalogue.hpp +++ b/Machines/Utility/ROMCatalogue.hpp @@ -118,6 +118,8 @@ enum Name { EnterpriseEPDOS, EnterpriseEXDOS, + EnterpriseEPFILEIO, + // Macintosh. Macintosh128k, Macintosh512k, diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index d60957154..6697271b9 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1148,6 +1148,9 @@ 4BCE0060227D39AB000CA200 /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCE005E227D39AB000CA200 /* Video.cpp */; }; 4BCE1DF125D4C3FA00AE7A2B /* Bus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCE1DEF25D4C3FA00AE7A2B /* Bus.cpp */; }; 4BCE1DF225D4C3FA00AE7A2B /* Bus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCE1DEF25D4C3FA00AE7A2B /* Bus.cpp */; }; + 4BCF1ACF2ECE759000109999 /* FileBundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCF1ACD2ECE759000109999 /* FileBundle.cpp */; }; + 4BCF1AD02ECE759000109999 /* FileBundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCF1ACD2ECE759000109999 /* FileBundle.cpp */; }; + 4BCF1AD12ECE759000109999 /* FileBundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCF1ACD2ECE759000109999 /* FileBundle.cpp */; }; 4BCF1FA41DADC3DD0039D2E7 /* Oric.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCF1FA21DADC3DD0039D2E7 /* Oric.cpp */; }; 4BD0FBC3233706A200148981 /* CSApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD0FBC2233706A200148981 /* CSApplication.m */; }; 4BD191F52191180E0042E144 /* ScanTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD191F22191180E0042E144 /* ScanTarget.cpp */; }; @@ -2426,6 +2429,8 @@ 4BCE005F227D39AB000CA200 /* Video.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Video.hpp; sourceTree = ""; }; 4BCE1DEF25D4C3FA00AE7A2B /* Bus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Bus.cpp; sourceTree = ""; }; 4BCE1DF025D4C3FA00AE7A2B /* Bus.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Bus.hpp; sourceTree = ""; }; + 4BCF1ACC2ECE759000109999 /* FileBundle.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FileBundle.hpp; sourceTree = ""; }; + 4BCF1ACD2ECE759000109999 /* FileBundle.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FileBundle.cpp; sourceTree = ""; }; 4BCF1FA21DADC3DD0039D2E7 /* Oric.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Oric.cpp; sourceTree = ""; }; 4BCF1FA31DADC3DD0039D2E7 /* Oric.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Oric.hpp; sourceTree = ""; }; 4BD060A51FE49D3C006E14BE /* Speaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Speaker.hpp; sourceTree = ""; }; @@ -3589,6 +3594,7 @@ 4BEE0A691D72496600532C7B /* Cartridge */, 4B8805F81DCFF6CD003085B1 /* Data */, 4BAB62AA1D3272D200DF5BA0 /* Disk */, + 4BCF1ACE2ECE759000109999 /* FileBundle */, 4B6AAEA1230E3E1D0078E864 /* MassStorage */, 4B8DD3832634D37E00B3C866 /* State */, 4B69FB3A1C4D908A00B5F0AA /* Tape */, @@ -5291,6 +5297,15 @@ path = ADB; sourceTree = ""; }; + 4BCF1ACE2ECE759000109999 /* FileBundle */ = { + isa = PBXGroup; + children = ( + 4BCF1ACC2ECE759000109999 /* FileBundle.hpp */, + 4BCF1ACD2ECE759000109999 /* FileBundle.cpp */, + ); + path = FileBundle; + sourceTree = ""; + }; 4BCF1FA51DADC3E10039D2E7 /* Oric */ = { isa = PBXGroup; children = ( @@ -6199,6 +6214,7 @@ 4B65086122F4CFE0009C1100 /* Keyboard.cpp in Sources */, 4BBB70A9202014E2002FE009 /* MultiProducer.cpp in Sources */, 4B2E86BF25D74F160024F1E9 /* Mouse.cpp in Sources */, + 4BCF1AD12ECE759000109999 /* FileBundle.cpp in Sources */, 4B6ED2F1208E2F8A0047B343 /* WOZ.cpp in Sources */, 4B5D5C9825F56FC7001B4623 /* Spectrum.cpp in Sources */, 4B7C681727517A59001671EC /* Sprites.cpp in Sources */, @@ -6499,6 +6515,7 @@ 4BEDA3BF25B25563000C2DBD /* Decoder.cpp in Sources */, 4B051C95266EF50200CA44E8 /* AppleIIController.swift in Sources */, 4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */, + 4BCF1ACF2ECE759000109999 /* FileBundle.cpp in Sources */, 4BE8EB6625C750B50040BC40 /* DAT.cpp in Sources */, 4BBFFEE61F7B27F1005F3FEB /* TrackSerialiser.cpp in Sources */, 4B8855A52E84D51B00E251DD /* SAA5050.cpp in Sources */, @@ -6859,6 +6876,7 @@ 4B778F4023A5F1910000D260 /* z8530.cpp in Sources */, 4B778EFD23A5EB8E0000D260 /* AppleDSK.cpp in Sources */, 4B7752B728217EF40073E2C5 /* Chipset.cpp in Sources */, + 4BCF1AD02ECE759000109999 /* FileBundle.cpp in Sources */, 4B06AAF72C64606E0034D014 /* DiskII.cpp in Sources */, 4B778EFB23A5EB7E0000D260 /* HFE.cpp in Sources */, 4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */, diff --git a/Storage/FileBundle/FileBundle.cpp b/Storage/FileBundle/FileBundle.cpp new file mode 100644 index 000000000..93fcec767 --- /dev/null +++ b/Storage/FileBundle/FileBundle.cpp @@ -0,0 +1,9 @@ +// +// FileBundle.cpp +// Clock Signal +// +// Created by Thomas Harte on 19/11/2025. +// Copyright © 2025 Thomas Harte. All rights reserved. +// + +#include "FileBundle.hpp" diff --git a/Storage/FileBundle/FileBundle.hpp b/Storage/FileBundle/FileBundle.hpp new file mode 100644 index 000000000..07a7e0dff --- /dev/null +++ b/Storage/FileBundle/FileBundle.hpp @@ -0,0 +1,31 @@ +// +// FileBundle.hpp +// Clock Signal +// +// Created by Thomas Harte on 19/11/2025. +// Copyright © 2025 Thomas Harte. All rights reserved. +// + +#pragma once + +namespace Storage::FileBundle { + +/*! + A File Bundle is a collection of individual files, abstracted from whatever media they might + be one. + + Initial motivation is allowing some machines direct local filesystem access. An attempt has + been made to draft this in such a way as to allow it to do things like expose ZIP files as + bundles in the future. +*/ +struct FileBundle { + + +}; + + +struct LocalFSFileBundle { + +}; + +}; From febff84421d436d4fe8e4e3352ae238ce3f519c2 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 19 Nov 2025 17:09:55 -0500 Subject: [PATCH 04/48] Add file bundles as an undefined concept. --- Analyser/Static/StaticAnalyser.cpp | 6 ++++++ Storage/FileBundle/FileBundle.hpp | 10 ++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Analyser/Static/StaticAnalyser.cpp b/Analyser/Static/StaticAnalyser.cpp index 2cd990f7c..3e9a5b409 100644 --- a/Analyser/Static/StaticAnalyser.cpp +++ b/Analyser/Static/StaticAnalyser.cpp @@ -63,6 +63,9 @@ #include "Storage/Disk/DiskImage/Formats/STX.hpp" #include "Storage/Disk/DiskImage/Formats/WOZ.hpp" +// File Bundles. +#include "Storage/FileBundle/FileBundle.hpp" + // Mass Storage Devices (i.e. usually, hard disks) #include "Storage/MassStorage/Formats/DAT.hpp" #include "Storage/MassStorage/Formats/DSK.hpp" @@ -123,6 +126,8 @@ public: media.cartridges.push_back(instance); } else if constexpr (std::is_base_of_v) { media.mass_storage_devices.push_back(instance); + } else if constexpr (std::is_base_of_v) { + media.file_bundles.push_back(instance); } else { static_assert(always_false_v, "Unexpected type encountered."); } @@ -215,6 +220,7 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform:: accumulator.try_standard(TargetPlatform::MSX, "cas"); accumulator.try_standard(TargetPlatform::AmstradCPC, "cdt"); accumulator.try_standard(TargetPlatform::Coleco, "col"); + accumulator.try_standard(TargetPlatform::Enterprise, "com"); accumulator.try_standard(TargetPlatform::AllTape, "csw"); accumulator.try_standard>(TargetPlatform::Commodore8bit, "d64"); diff --git a/Storage/FileBundle/FileBundle.hpp b/Storage/FileBundle/FileBundle.hpp index 07a7e0dff..248b6c211 100644 --- a/Storage/FileBundle/FileBundle.hpp +++ b/Storage/FileBundle/FileBundle.hpp @@ -8,6 +8,8 @@ #pragma once +#include + namespace Storage::FileBundle { /*! @@ -19,13 +21,13 @@ namespace Storage::FileBundle { bundles in the future. */ struct FileBundle { - - }; -struct LocalFSFileBundle { - +struct LocalFSFileBundle: public FileBundle { + LocalFSFileBundle(const std::string &to_contain) { + (void)to_contain; + } }; }; From c876bcb849d6a41a6e32756a055b78ef9a071e8b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 19 Nov 2025 17:50:16 -0500 Subject: [PATCH 05/48] Ensure appropriate-looking .com files get to the Enterprise. --- Analyser/Static/Enterprise/StaticAnalyser.cpp | 20 +++++++++++++++-- OSBindings/Mac/Clock Signal/Info.plist | 22 +++++++++++++++++++ Storage/FileBundle/FileBundle.hpp | 16 ++++++++++++-- 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/Analyser/Static/Enterprise/StaticAnalyser.cpp b/Analyser/Static/Enterprise/StaticAnalyser.cpp index 6223d119f..a9e2ef43b 100644 --- a/Analyser/Static/Enterprise/StaticAnalyser.cpp +++ b/Analyser/Static/Enterprise/StaticAnalyser.cpp @@ -33,7 +33,7 @@ Analyser::Static::TargetList Analyser::Static::Enterprise::GetTargets( bool ) { // This analyser can comprehend disks only. - if(media.disks.empty()) return {}; + if(media.disks.empty() && media.file_bundles.empty()) return {}; // Otherwise, assume a return will happen. Analyser::Static::TargetList targets; @@ -86,7 +86,23 @@ Analyser::Static::TargetList Analyser::Static::Enterprise::GetTargets( } } - targets.push_back(std::unique_ptr(target)); + if(!media.file_bundles.empty()) { + auto file = media.file_bundles.front()->key_file(); + + // Check for a .COM by inspecting the header. + const uint16_t type = file.get_le(); + const uint16_t size = file.get_le(); + // To consider: all the COMs I've seen also now have 12 bytes of 0 padding. + // Test that? + + if(type != 0x0500 || size != file.stats().st_size - 16) { + target->media.file_bundles.clear(); + } + } + + if(!target->media.empty()) { + targets.push_back(std::unique_ptr(target)); + } return targets; } diff --git a/OSBindings/Mac/Clock Signal/Info.plist b/OSBindings/Mac/Clock Signal/Info.plist index fbdc0f2b3..a4c44e12e 100644 --- a/OSBindings/Mac/Clock Signal/Info.plist +++ b/OSBindings/Mac/Clock Signal/Info.plist @@ -807,6 +807,28 @@ NSDocumentClass $(PRODUCT_MODULE_NAME).MachineDocument + + CFBundleTypeExtensions + + com + + CFBundleTypeIconFile + + CFBundleTypeName + Enterprise Executable + CFBundleTypeOSTypes + + ???? + + CFBundleTypeRole + Viewer + LSHandlerRank + Owner + LSTypeIsPackage + + NSDocumentClass + $(PRODUCT_MODULE_NAME).MachineDocument + CFBundleExecutable $(EXECUTABLE_NAME) diff --git a/Storage/FileBundle/FileBundle.hpp b/Storage/FileBundle/FileBundle.hpp index 248b6c211..60d64c7d8 100644 --- a/Storage/FileBundle/FileBundle.hpp +++ b/Storage/FileBundle/FileBundle.hpp @@ -8,6 +8,7 @@ #pragma once +#include "Storage/FileHolder.hpp" #include namespace Storage::FileBundle { @@ -21,13 +22,24 @@ namespace Storage::FileBundle { bundles in the future. */ struct FileBundle { + virtual FileHolder key_file() = 0; + virtual uint32_t open(const std::string &) = 0; }; struct LocalFSFileBundle: public FileBundle { - LocalFSFileBundle(const std::string &to_contain) { - (void)to_contain; + LocalFSFileBundle(const std::string &to_contain) : to_contain_(to_contain) {} + + FileHolder key_file() override { + return FileHolder(to_contain_); } + + uint32_t open(const std::string &) override { + return 0; + } + +private: + std::string to_contain_; }; }; From e44cbcc1d5f9eb85bae57c9467e39f78621aaa4e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 19 Nov 2025 21:08:46 -0500 Subject: [PATCH 06/48] Add to all project files. --- OSBindings/Qt/clksignal.pro | 2 ++ OSBindings/SDL/SConstruct | 1 + cmake/CLK_SOURCES.cmake | 1 + 3 files changed, 4 insertions(+) diff --git a/OSBindings/Qt/clksignal.pro b/OSBindings/Qt/clksignal.pro index 771b07461..d1837f1bc 100644 --- a/OSBindings/Qt/clksignal.pro +++ b/OSBindings/Qt/clksignal.pro @@ -146,6 +146,7 @@ SOURCES += \ $$SRC/Storage/Disk/Encodings/MFM/*.cpp \ $$SRC/Storage/Disk/Parsers/*.cpp \ $$SRC/Storage/Disk/Track/*.cpp \ + $$SRC/Storage/FileBundle/*.cpp \ $$SRC/Storage/MassStorage/*.cpp \ $$SRC/Storage/MassStorage/Encodings/*.cpp \ $$SRC/Storage/MassStorage/Formats/*.cpp \ @@ -303,6 +304,7 @@ HEADERS += \ $$SRC/Storage/Disk/Encodings/MFM/*.hpp \ $$SRC/Storage/Disk/Parsers/*.hpp \ $$SRC/Storage/Disk/Track/*.hpp \ + $$SRC/Storage/FileBundle/*.hpp \ $$SRC/Storage/MassStorage/*.hpp \ $$SRC/Storage/MassStorage/Encodings/*.hpp \ $$SRC/Storage/MassStorage/Formats/*.hpp \ diff --git a/OSBindings/SDL/SConstruct b/OSBindings/SDL/SConstruct index a98f6580b..07f3728f7 100644 --- a/OSBindings/SDL/SConstruct +++ b/OSBindings/SDL/SConstruct @@ -134,6 +134,7 @@ SOURCES += glob.glob('../../Storage/Disk/Encodings/MFM/*.cpp') SOURCES += glob.glob('../../Storage/Disk/Parsers/*.cpp') SOURCES += glob.glob('../../Storage/Disk/Track/*.cpp') SOURCES += glob.glob('../../Storage/Disk/Data/*.cpp') +SOURCES += glob.glob('../../Storage/FileBundle/*.cpp') SOURCES += glob.glob('../../Storage/MassStorage/*.cpp') SOURCES += glob.glob('../../Storage/MassStorage/Encodings/*.cpp') SOURCES += glob.glob('../../Storage/MassStorage/Formats/*.cpp') diff --git a/cmake/CLK_SOURCES.cmake b/cmake/CLK_SOURCES.cmake index 195845037..184021f8a 100644 --- a/cmake/CLK_SOURCES.cmake +++ b/cmake/CLK_SOURCES.cmake @@ -224,6 +224,7 @@ set(CLK_SOURCES Storage/Disk/Track/PCMTrack.cpp Storage/Disk/Track/TrackSerialiser.cpp Storage/Disk/Track/UnformattedTrack.cpp + Storage/FileBundle/FileBundle.cpp Storage/FileHolder.cpp Storage/MassStorage/Encodings/MacintoshVolume.cpp Storage/MassStorage/Formats/DAT.cpp From 25bf7df4d1413bd643bc4770790c1ad9cc12c84f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 19 Nov 2025 21:53:53 -0500 Subject: [PATCH 07/48] Install FILE IO ROM and list out syscall points. --- Machines/Enterprise/Enterprise.cpp | 41 ++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/Machines/Enterprise/Enterprise.cpp b/Machines/Enterprise/Enterprise.cpp index 80eb8d417..85ca98be6 100644 --- a/Machines/Enterprise/Enterprise.cpp +++ b/Machines/Enterprise/Enterprise.cpp @@ -174,6 +174,12 @@ public: default: break; } + // Ensure a briddge into the local filing system if requested. + const bool needs_host_bridge = !target.media.file_bundles.empty(); + if(needs_host_bridge) { + request = request && ROM::Request(ROM::Name::EnterpriseEPFILEIO); + } + // Get and validate ROMs. auto roms = rom_fetcher(request); if(!request.validate(roms)) { @@ -228,6 +234,14 @@ public: memcpy(exdos_rom_.data(), exdos->second.data(), std::min(exdos_rom_.size(), exdos->second.size())); } + // Possibly grab the file IO ROM. + const auto file_io = roms.find(ROM::Name::EnterpriseEPFILEIO); + file_io_rom_.fill(0xff); + if(file_io != roms.end()) { + memcpy(file_io_rom_.data(), file_io->second.data(), std::min(file_io_rom_.size(), file_io->second.size())); + find_file_io_hooks(); + } + // Seed key state. clear_all_keys(); @@ -575,6 +589,7 @@ private: std::array basic_; std::array exdos_rom_; std::array epdos_rom_; + std::array file_io_rom_; const uint8_t min_ram_slot_; const uint8_t *read_pointers_[4] = {nullptr, nullptr, nullptr, nullptr}; @@ -599,6 +614,7 @@ private: if(page_rom(offset, 16, basic_)) return; if(page_rom(offset, 32, exdos_rom_)) return; if(page_rom(offset, 48, epdos_rom_)) return; + if(page_rom(offset, 64, file_io_rom_)) return; // Of whatever size of RAM I've declared above, use only the final portion. // This correlated with Nick always having been handed the final 64kb and, @@ -620,8 +636,30 @@ private: write_pointers_[slot] = write ? write - (slot * 0x4000) : nullptr; } - // MARK: - Memory Timing + // MARK: - Host file access hooks + void find_file_io_hooks() { + static constexpr uint8_t syscall[] = { + 0xed, 0xfe, 0xfe + }; + auto begin = file_io_rom_.begin(); + while(true) { + begin = std::search( + begin, file_io_rom_.end(), + std::begin(syscall), std::end(syscall) + ); + + if(begin == file_io_rom_.end()) { + break; + } + + const auto offset = begin - file_io_rom_.begin(); + printf("Call %d at %02x\n", file_io_rom_[offset + 3], offset); + begin += 3; + } + } + + // MARK: - Memory Timing // The wait mode affects all memory accesses _outside of the video area_. enum class WaitMode { None, @@ -710,7 +748,6 @@ private: } // MARK: - Interrupts - uint8_t interrupt_mask_ = 0x00, interrupt_state_ = 0x00; void set_interrupts(const uint8_t mask, const HalfCycles offset = HalfCycles(0)) { interrupt_state_ |= uint8_t(mask); From bb7059a9e12f76d314dc5de8e456d0f10f1d8c95 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 20 Nov 2025 12:41:41 -0500 Subject: [PATCH 08/48] KEEPSAKE. --- Machines/Enterprise/EXOSCodes.hpp | 142 ++++++++++++++++++ Machines/Enterprise/Enterprise.cpp | 14 ++ Machines/Enterprise/HostFSHandler.cpp | 21 +++ Machines/Enterprise/HostFSHandler.hpp | 23 +++ .../Clock Signal.xcodeproj/project.pbxproj | 21 +++ 5 files changed, 221 insertions(+) create mode 100644 Machines/Enterprise/EXOSCodes.hpp create mode 100644 Machines/Enterprise/HostFSHandler.cpp create mode 100644 Machines/Enterprise/HostFSHandler.hpp diff --git a/Machines/Enterprise/EXOSCodes.hpp b/Machines/Enterprise/EXOSCodes.hpp new file mode 100644 index 000000000..bddb7b1b4 --- /dev/null +++ b/Machines/Enterprise/EXOSCodes.hpp @@ -0,0 +1,142 @@ +// +// EXOSCodes.hpp +// Clock Signal +// +// Created by Thomas Harte on 20/11/2025. +// Copyright © 2025 Thomas Harte. All rights reserved. +// + +#pragma once + +// Various EXOS codes, transcribed from EXOS20_technical_information.pdf via archive.org, +// which appears to be a compilation of original documentation so page numbers below +// refer to the page within the PDF. Numbers printed on the in-document pages are inconsistent. + +namespace Enterprise::EXOS { + +// Page 67. +enum class Function: uint8_t { + ResetSystem = 0, // RESET + OpenChannel = 1, // OPEN + CreateChannel = 2, // CREAT + CloseChannel = 3, // CLOSE + DestroyChannel = 4, // DEST + ReadCharacter = 5, // RDCH + ReadBlock = 6, // RDBLK + WriteCharacter = 7, // WRCH + WriteBlock = 8, // WRBLK + ReadStatus = 9, // RSTAT + SetChannelStatus = 10, // SSTAT + SpecialFunction = 11, // SFUNC + + SetReadToggleEXOSVariable = 16, // EVAR + CaptureChannel = 17, // CAPT + RedirectChannel = 18, // REDIR + SetDefaultDevice = 19, // DDEV + ReturnSystemStatus = 20, // SYSS + LinkDevices = 21, // LINK + ReadEXOSBoundary = 22, // READB + SetUSERBoundary = 23, // SETB, + AllocateSegment = 24, // ALLOC, + FreeSegment = 25, // FREE + LocateROMs = 26, // ROMS + AllocateChannelBuffer = 27, // BUFF + ReturnErrorMessage = 28, // ERRMSG +}; + +// Pages 68–70. +enum class Error: uint8_t { + // + // General errors returned by the EXOS kernel. + // + InvalidFunctionCode = 0xff, // IFUNC + FunctionCallNotAllowed = 0xfe, // ILLFN + InvalidFunctionName = 0xfd, // INAME + InsufficientStack = 0xfc, // STACK + ChannelIllegalOrDoesNotExist = 0xfb, // ICHAN + DeviceDoesNotExist = 0xfa, // NODEV + ChannelAlreadyExists = 0xf9, // CHANX + NoAllocateBufferCallMade = 0xf8, // NOBUF + BadAllocateBufferParameters = 0xf7, // BADAL + InsufficientRAMForBuffer = 0xf6, // NORAM + InsufficientVideoRAM = 0xf5, // NOVID + NoFreeSegments = 0xf4, // NOSEG + InvalidSegment = 0xf3, // ISEG + InvalidUserBoundary = 0xf2, // IBOUN + InvalidEXOSVariableNumber = 0xf1, // IVAR + InvalidDesviceDescriptorType = 0xf0, // IDESC + + // + // General errors returned by various devices. + // + InvalidSpecialFunctionCode = 0xef, // ISPEC + AttemptToOpenSecondChannel = 0xee, // 2NDCH + FunctionNotSupported = 0xed, // NOFN + InvalidEscapeCharacter = 0xec, // ESC + StopKeyPressed = 0xeb, // STOP + + // + // File related errors. + // + FileDoesNotExist = 0xea, // NOFIL + FileAlreadyExists = 0xe9, // EXFIL + FileAlreadyOpen = 0xe8, // FOPEN + EndOfFileMetInRead = 0xe7, // EOF + FileIsTooBig = 0xe6, // FSIZE + InvalidFilePointerValue = 0xe5, // FPTR + ProtectionViolation = 0xe4, // PROT + + // + // Keyboard errors. + // + InvalidFunctionKeyNumber = 0xe3, // KFKEY + RunOutOfFunctionKeySpace = 0xe2, // KFSPC + + // + // Sound errors. + // + EnvelopeInvalidOrTooBig = 0xe1, // SENV + NotEnoughRoomToDefineEnvelope = 0xe0, // SENDBF + EnvelopeStorageRequestedTooSmall = 0xdf, // SENFLO + SoundQueueFull = 0xde, // SQFUL + + // + // Video errors. + // + InvalidRowNumberToScroll = 0xdd, // VROW + AttemptToMoveCursorOffPage = 0xdc, // VCURS + InvalidColourPassedToINKOrPAPER = 0xdb, // VCOLR + InvalidXOrYSizeToOPEN = 0xda, // VSIZE + InvalidVideoModeToOPEN = 0xd9, // VMODE + BadParameterToDISPLAY = 0xdb, // VDISP, and officially 'naff' rather than 'bad' + NotEnoughRowsInPageToDISPLAY = 0xd7, // VDSP2 + AttemptToMoveBeamOffPage = 0xd6, // VBEAM + LineStyleTooBig = 0xd5, // VLSTY + LineModeTooBig = 0xd4, // VLMOD + CantDisplayCharacterOrGraphic = 0xd3, // VCHAR + + // + // Serial errors. + // + InvalidBaudRate = 0xd2, // BAUD + + // + // Editor errors. + // + InvalidVideoPageForOPEN = 0xd1, // EVID + TroubleInCommunicatingWithKeyboard = 0xd0, // EKEY + InvalidCoordinatesForPosition = 0xcf, // ECURS + + // + // Cassette errors. + // + CRCErrorFromCassetteDriver = 0xce, // CCRC + + // + // Network errors + // + SerialDeviceOpenCannotUseNetwork = 0xcd, // SEROP + ADDR_NETNotSetUp = 0xcc, // NOADR +}; + +} diff --git a/Machines/Enterprise/Enterprise.cpp b/Machines/Enterprise/Enterprise.cpp index 85ca98be6..d8cd883ec 100644 --- a/Machines/Enterprise/Enterprise.cpp +++ b/Machines/Enterprise/Enterprise.cpp @@ -10,6 +10,7 @@ #include "Dave.hpp" #include "EXDos.hpp" +#include "HostFSHandler.hpp" #include "Keyboard.hpp" #include "Nick.hpp" @@ -23,6 +24,8 @@ #include "Outputs/Speaker/Implementation/LowpassSpeaker.hpp" #include "Processors/Z80/Z80.hpp" +#include + namespace { using Logger = Log::Logger; } @@ -584,6 +587,7 @@ public: private: // MARK: - Memory layout + std::array ram_{}; std::array exos_; std::array basic_; @@ -669,6 +673,7 @@ private: bool is_video_[4]{}; // MARK: - ScanProducer + void set_scan_target(Outputs::Display::ScanTarget *const scan_target) override { nick_.last_valid()->set_scan_target(scan_target); } @@ -779,9 +784,17 @@ private: } // MARK: - EXDos card. + EXDos exdos_; + // MARK: - Host FS. + + HostFSHandler host_fs_; + std::unordered_set trap_locations_; + bool test_host_fs_traps_ = false; + // MARK: - Activity Source + void set_activity_observer([[maybe_unused]] Activity::Observer *const observer) final { if constexpr (has_disk_controller) { exdos_.set_activity_observer(observer); @@ -789,6 +802,7 @@ private: } // MARK: - Configuration options. + std::unique_ptr get_options() const final { auto options = std::make_unique(Configurable::OptionsType::UserFriendly); options->output = get_video_signal_configurable(); diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp new file mode 100644 index 000000000..c3ed12358 --- /dev/null +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -0,0 +1,21 @@ +// +// HostFSHandler.cpp +// Clock Signal +// +// Created by Thomas Harte on 20/11/2025. +// Copyright © 2025 Thomas Harte. All rights reserved. +// + +#include "HostFSHandler.hpp" +#include "EXOSCodes.hpp" + +using namespace Enterprise; + +HostFSHandler::HostFSHandler(uint8_t *ram) : ram_(ram) {} + +void HostFSHandler::perform(uint8_t function, uint8_t &a, uint16_t &bc, uint16_t &de) { + (void)function; + (void)a; + (void)bc; + (void)de; +} diff --git a/Machines/Enterprise/HostFSHandler.hpp b/Machines/Enterprise/HostFSHandler.hpp new file mode 100644 index 000000000..affec6ac3 --- /dev/null +++ b/Machines/Enterprise/HostFSHandler.hpp @@ -0,0 +1,23 @@ +// +// HostFSHandler.hpp +// Clock Signal +// +// Created by Thomas Harte on 20/11/2025. +// Copyright © 2025 Thomas Harte. All rights reserved. +// + +#pragma once + +#include + +namespace Enterprise { + +struct HostFSHandler { + HostFSHandler(uint8_t *ram); + void perform(uint8_t function, uint8_t &a, uint16_t &bc, uint16_t &de); + +private: + uint8_t *ram_; +}; + +}; diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 6697271b9..da8842f5c 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1148,9 +1148,15 @@ 4BCE0060227D39AB000CA200 /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCE005E227D39AB000CA200 /* Video.cpp */; }; 4BCE1DF125D4C3FA00AE7A2B /* Bus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCE1DEF25D4C3FA00AE7A2B /* Bus.cpp */; }; 4BCE1DF225D4C3FA00AE7A2B /* Bus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCE1DEF25D4C3FA00AE7A2B /* Bus.cpp */; }; +<<<<<<< Updated upstream 4BCF1ACF2ECE759000109999 /* FileBundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCF1ACD2ECE759000109999 /* FileBundle.cpp */; }; 4BCF1AD02ECE759000109999 /* FileBundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCF1ACD2ECE759000109999 /* FileBundle.cpp */; }; 4BCF1AD12ECE759000109999 /* FileBundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCF1ACD2ECE759000109999 /* FileBundle.cpp */; }; +======= + 4BCF1AD52ECF884100109999 /* HostFSHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCF1AD42ECF884100109999 /* HostFSHandler.cpp */; }; + 4BCF1AD62ECF884100109999 /* HostFSHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCF1AD42ECF884100109999 /* HostFSHandler.cpp */; }; + 4BCF1AD72ECF884100109999 /* HostFSHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCF1AD42ECF884100109999 /* HostFSHandler.cpp */; }; +>>>>>>> Stashed changes 4BCF1FA41DADC3DD0039D2E7 /* Oric.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCF1FA21DADC3DD0039D2E7 /* Oric.cpp */; }; 4BD0FBC3233706A200148981 /* CSApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD0FBC2233706A200148981 /* CSApplication.m */; }; 4BD191F52191180E0042E144 /* ScanTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD191F22191180E0042E144 /* ScanTarget.cpp */; }; @@ -2429,8 +2435,14 @@ 4BCE005F227D39AB000CA200 /* Video.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Video.hpp; sourceTree = ""; }; 4BCE1DEF25D4C3FA00AE7A2B /* Bus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Bus.cpp; sourceTree = ""; }; 4BCE1DF025D4C3FA00AE7A2B /* Bus.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Bus.hpp; sourceTree = ""; }; +<<<<<<< Updated upstream 4BCF1ACC2ECE759000109999 /* FileBundle.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FileBundle.hpp; sourceTree = ""; }; 4BCF1ACD2ECE759000109999 /* FileBundle.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FileBundle.cpp; sourceTree = ""; }; +======= + 4BCF1AD22ECF743500109999 /* EXOSCodes.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = EXOSCodes.hpp; sourceTree = ""; }; + 4BCF1AD32ECF884100109999 /* HostFSHandler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = HostFSHandler.hpp; sourceTree = ""; }; + 4BCF1AD42ECF884100109999 /* HostFSHandler.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = HostFSHandler.cpp; sourceTree = ""; }; +>>>>>>> Stashed changes 4BCF1FA21DADC3DD0039D2E7 /* Oric.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Oric.cpp; sourceTree = ""; }; 4BCF1FA31DADC3DD0039D2E7 /* Oric.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Oric.hpp; sourceTree = ""; }; 4BD060A51FE49D3C006E14BE /* Speaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Speaker.hpp; sourceTree = ""; }; @@ -2706,12 +2718,18 @@ 4BFEA2ED2682A7B900EBF94C /* Dave.cpp */, 4B051CA12676F52200CA44E8 /* Enterprise.cpp */, 4B051CB42680158600CA44E8 /* EXDos.cpp */, + 4BCF1AD42ECF884100109999 /* HostFSHandler.cpp */, 4B051CAE267C1CA200CA44E8 /* Keyboard.cpp */, 4B051CAA26783E2000CA44E8 /* Nick.cpp */, 4BFEA2EE2682A7B900EBF94C /* Dave.hpp */, 4B051CA02676F52200CA44E8 /* Enterprise.hpp */, 4B051CB52680158600CA44E8 /* EXDos.hpp */, +<<<<<<< Updated upstream 4B3583DB2DB965FA00A24128 /* HostFS.hpp */, +======= + 4BCF1AD22ECF743500109999 /* EXOSCodes.hpp */, + 4BCF1AD32ECF884100109999 /* HostFSHandler.hpp */, +>>>>>>> Stashed changes 4B051CAF267C1CA200CA44E8 /* Keyboard.hpp */, 4B051CAB26783E2000CA44E8 /* Nick.hpp */, ); @@ -6357,6 +6375,7 @@ 4B055AC41FAE9AE80060FFFF /* Keyboard.cpp in Sources */, 4B8DF506254E3C9D00F3433C /* ADB.cpp in Sources */, 4B055A941FAE85B50060FFFF /* CommodoreROM.cpp in Sources */, + 4BCF1AD72ECF884100109999 /* HostFSHandler.cpp in Sources */, 4BBB70A5202011C2002FE009 /* MultiMediaTarget.cpp in Sources */, 4B8318BC22D3E588006DB630 /* DisplayMetrics.cpp in Sources */, 4BEDA40E25B2844B000C2DBD /* Decoder.cpp in Sources */, @@ -6450,6 +6469,7 @@ 4B1082C42C1F5E7D00B07C5D /* CSL.cpp in Sources */, 4B0ACC3023775819008902D0 /* TIASound.cpp in Sources */, 4B7136861F78724F008B8ED9 /* Encoder.cpp in Sources */, + 4BCF1AD62ECF884100109999 /* HostFSHandler.cpp in Sources */, 4B0E04EA1FC9E5DA00F43484 /* CAS.cpp in Sources */, 4B7A90ED20410A85008514A2 /* StaticAnalyser.cpp in Sources */, 4B58601E1F806AB200AEE2E3 /* MFMSectorDump.cpp in Sources */, @@ -6879,6 +6899,7 @@ 4BCF1AD02ECE759000109999 /* FileBundle.cpp in Sources */, 4B06AAF72C64606E0034D014 /* DiskII.cpp in Sources */, 4B778EFB23A5EB7E0000D260 /* HFE.cpp in Sources */, + 4BCF1AD52ECF884100109999 /* HostFSHandler.cpp in Sources */, 4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */, 4B0DA67D282DCDF300C12F17 /* Instruction.cpp in Sources */, 4B06AAE12C645F8B0034D014 /* Video.cpp in Sources */, From ae89c66b17867b01443d4674a33d533638444177 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 20 Nov 2025 12:32:00 -0500 Subject: [PATCH 09/48] Transcribe basic function and error codes. --- Machines/Enterprise/EXOSCodes.hpp | 142 ++++++++++++++++++ .../Clock Signal.xcodeproj/project.pbxproj | 2 + 2 files changed, 144 insertions(+) create mode 100644 Machines/Enterprise/EXOSCodes.hpp diff --git a/Machines/Enterprise/EXOSCodes.hpp b/Machines/Enterprise/EXOSCodes.hpp new file mode 100644 index 000000000..c3e46998a --- /dev/null +++ b/Machines/Enterprise/EXOSCodes.hpp @@ -0,0 +1,142 @@ +// +// EXOSCodes.hpp +// Clock Signal +// +// Created by Thomas Harte on 20/11/2025. +// Copyright © 2025 Thomas Harte. All rights reserved. +// + +#pragma once + +// Various EXOS codes, transcribed from EXOS20_technical_information.pdf via archive.org, +// which appears to be a compilation of original documentation so page numbers below +// refer to the page within the PDF. Numbers printed on the in-document pages are inconsistent. + +namespace Enterprise::EXOS { + +// Page 67. +enum class Function: uint8_t { + ResetSystem = 0, // RESET + OpenChannel = 1, // OPEN + CreateChannel = 2, // CREAT + CloseChannel = 3, // CLOSE + DestroyChannel = 4, // DEST + ReadCharacter = 5, // RDCH + ReadBlock = 6, // RDBLK + WriteCharacter = 7, // WRCH + WriteBlock = 8, // WRBLK + ReadStatus = 9, // RSTAT + SetChannelStatus = 10, // SSTAT + SpecialFunction = 11, // SFUNC + + SetReadToggleEXOSVariable = 16, // EVAR + CaptureChannel = 17, // CAPT + RedirectChannel = 18, // REDIR + SetDefaultDevice = 19, // DDEV + ReturnSystemStatus = 20, // SYSS + LinkDevices = 21, // LINK + ReadEXOSBoundary = 22, // READB + SetUSERBoundary = 23, // SETB, + AllocateSegment = 24, // ALLOC, + FreeSegment = 25, // FREE + LocateROMs = 26, // ROMS + AllocateChannelBuffer = 27, // BUFF + ReturnErrorMessage = 28, // ERRMSG +}; + +// Pages 68–70. +enum class Error: uint8_t { + // + // General errors returned by the EXOS kernel. + // + InvalidFunctionCode = 0xff, // IFUNC + FunctionCallNotAllowed = 0xfe, // ILLFN + InvalidFunctionName = 0xfd, // INAME + InsufficientStack = 0xfc, // STACK + ChannelIllegalOrDoesNotExist = 0xfb, // ICHAN + DeviceDoesNotExist = 0xfa, // NODEV + ChannelAlreadyExists = 0xf9, // CHANX + NoAllocateBufferCallMade = 0xf8, // NOBUF + BadAllocateBufferParameters = 0xf7, // BADAL + InsufficientRAMForBuffer = 0xf6, // NORAM + InsufficientVideoRAM = 0xf5, // NOVID + NoFreeSegments = 0xf4, // NOSEG + InvalidSegment = 0xf3, // ISEG + InvalidUserBoundary = 0xf2, // IBOUN + InvalidEXOSVariableNumber = 0xf1, // IVAR + InvalidDesviceDescriptorType = 0xf0, // IDESC + + // + // General errors returned by various devices. + // + InvalidSpecialFunctionCode = 0xef, // ISPEC + AttemptToOpenSecondChannel = 0xee, // 2NDCH + FunctionNotSupported = 0xed, // NOFN + InvalidEscapeCharacter = 0xec, // ESC + StopKeyPressed = 0xeb, // STOP + + // + // File related errors. + // + FileDoesNotExist = 0xea, // NOFIL + FileAlreadyExists = 0xe9, // EXFIL + FileAlreadyOpen = 0xe8, // FOPEN + EndOfFileMetInRead = 0xe7, // EOF + FileIsTooBig = 0xe6, // FSIZE + InvalidFilePointerValue = 0xe5, // FPTR + ProtectionViolation = 0xe4, // PROT + + // + // Keyboard errors. + // + InvalidFunctionKeyNumber = 0xe3, // KFKEY + RunOutOfFunctionKeySpace = 0xe2, // KFSPC + + // + // Sound errors. + // + EnvelopeInvalidOrTooBig = 0xe1, // SENV + NotEnoughRoomToDefineEnvelope = 0xe0, // SENDBF + EnvelopeStorageRequestedTooSmall = 0xdf, // SENFLO + SoundQueueFull = 0xde, // SQFUL + + // + // Video errors. + // + InvalidRowNumberToScroll = 0xdd, // VROW + AttemptToMoveCursorOffPage = 0xdc, // VCURS + InvalidColourPassedToINKOrPAPER = 0xdb, // VCOLR + InvalidXOrYSizeToOPEN = 0xda, // VSIZE + InvalidVideoModeToOPEN = 0xd9, // VMODE + BadParameterToDISPLAY = 0xdb // VDISP, and officially 'naff' rather than 'bad' + NotEnoughRowsInPageToDISPLAY = 0xd7, // VDSP2 + AttemptToMoveBeamOffPage = 0xd6, // VBEAM + LineStyleTooBig = 0xd5, // VLSTY + LineModeTooBig = 0xd4, // VLMOD + CantDisplayCharacterOrGraphic = 0xd3, // VCHAR + + // + // Serial errors. + // + InvalidBaudRate = 0xd2, // BAUD + + // + // Editor errors. + // + InvalidVideoPageForOPEN = 0xd1, // EVID + TroubleInCommunicatingWithKeyboard = 0xd0, // EKEY + InvalidCoordinatesForPosition = 0xcf, // ECURS + + // + // Cassette errors. + // + CRCErrorFromCassetteDriver = 0xce, // CCRC + + // + // Network errors + // + SerialDeviceOpenCannotUseNetwork = 0xcd, // SEROP + ADDR_NETNotSetUp = 0xcc, // NOADR +}; + +} diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 6697271b9..5eb580012 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -2431,6 +2431,7 @@ 4BCE1DF025D4C3FA00AE7A2B /* Bus.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Bus.hpp; sourceTree = ""; }; 4BCF1ACC2ECE759000109999 /* FileBundle.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FileBundle.hpp; sourceTree = ""; }; 4BCF1ACD2ECE759000109999 /* FileBundle.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FileBundle.cpp; sourceTree = ""; }; + 4BCF1AD22ECF743500109999 /* EXOSCodes.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = EXOSCodes.hpp; sourceTree = ""; }; 4BCF1FA21DADC3DD0039D2E7 /* Oric.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Oric.cpp; sourceTree = ""; }; 4BCF1FA31DADC3DD0039D2E7 /* Oric.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Oric.hpp; sourceTree = ""; }; 4BD060A51FE49D3C006E14BE /* Speaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Speaker.hpp; sourceTree = ""; }; @@ -2714,6 +2715,7 @@ 4B3583DB2DB965FA00A24128 /* HostFS.hpp */, 4B051CAF267C1CA200CA44E8 /* Keyboard.hpp */, 4B051CAB26783E2000CA44E8 /* Nick.hpp */, + 4BCF1AD22ECF743500109999 /* EXOSCodes.hpp */, ); path = Enterprise; sourceTree = ""; From 4f09f38f2eee7d1576a524ef52d3b65783091f5c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 20 Nov 2025 12:49:21 -0500 Subject: [PATCH 10/48] Unify naming, record trap addresses. --- Machines/Enterprise/EXOSCodes.hpp | 4 -- Machines/Enterprise/Enterprise.cpp | 63 ++++++++++--------- Machines/Enterprise/HostFS.hpp | 23 ------- .../Clock Signal.xcodeproj/project.pbxproj | 3 - 4 files changed, 33 insertions(+), 60 deletions(-) delete mode 100644 Machines/Enterprise/HostFS.hpp diff --git a/Machines/Enterprise/EXOSCodes.hpp b/Machines/Enterprise/EXOSCodes.hpp index a50d2eb5a..bddb7b1b4 100644 --- a/Machines/Enterprise/EXOSCodes.hpp +++ b/Machines/Enterprise/EXOSCodes.hpp @@ -108,11 +108,7 @@ enum class Error: uint8_t { InvalidColourPassedToINKOrPAPER = 0xdb, // VCOLR InvalidXOrYSizeToOPEN = 0xda, // VSIZE InvalidVideoModeToOPEN = 0xd9, // VMODE -<<<<<<< HEAD - BadParameterToDISPLAY = 0xdb // VDISP, and officially 'naff' rather than 'bad' -======= BadParameterToDISPLAY = 0xdb, // VDISP, and officially 'naff' rather than 'bad' ->>>>>>> T NotEnoughRowsInPageToDISPLAY = 0xd7, // VDSP2 AttemptToMoveBeamOffPage = 0xd6, // VBEAM LineStyleTooBig = 0xd5, // VLSTY diff --git a/Machines/Enterprise/Enterprise.cpp b/Machines/Enterprise/Enterprise.cpp index d8cd883ec..2ac2bc6df 100644 --- a/Machines/Enterprise/Enterprise.cpp +++ b/Machines/Enterprise/Enterprise.cpp @@ -105,6 +105,7 @@ public: ConcreteMachine(const Analyser::Static::Enterprise::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : min_ram_slot_(min_ram_slot(target)), z80_(*this), + host_fs_(ram_.data()), // TODO: is this the correct section? nick_(ram_.end() - 65536), dave_audio_(audio_queue_), speaker_(dave_audio_) { @@ -238,11 +239,11 @@ public: } // Possibly grab the file IO ROM. - const auto file_io = roms.find(ROM::Name::EnterpriseEPFILEIO); - file_io_rom_.fill(0xff); - if(file_io != roms.end()) { - memcpy(file_io_rom_.data(), file_io->second.data(), std::min(file_io_rom_.size(), file_io->second.size())); - find_file_io_hooks(); + const auto host_fs = roms.find(ROM::Name::EnterpriseEPFILEIO); + host_fs_rom_.fill(0xff); + if(host_fs != roms.end()) { + memcpy(host_fs_rom_.data(), host_fs->second.data(), std::min(host_fs_rom_.size(), host_fs->second.size())); + find_host_fs_hooks(); } // Seed key state. @@ -593,7 +594,7 @@ private: std::array basic_; std::array exdos_rom_; std::array epdos_rom_; - std::array file_io_rom_; + std::array host_fs_rom_; const uint8_t min_ram_slot_; const uint8_t *read_pointers_[4] = {nullptr, nullptr, nullptr, nullptr}; @@ -618,7 +619,10 @@ private: if(page_rom(offset, 16, basic_)) return; if(page_rom(offset, 32, exdos_rom_)) return; if(page_rom(offset, 48, epdos_rom_)) return; - if(page_rom(offset, 64, file_io_rom_)) return; + if(page_rom(offset, 64, host_fs_rom_)) { + // TODO: test_host_fs_traps_. + return; + } // Of whatever size of RAM I've declared above, use only the final portion. // This correlated with Nick always having been handed the final 64kb and, @@ -640,29 +644,6 @@ private: write_pointers_[slot] = write ? write - (slot * 0x4000) : nullptr; } - // MARK: - Host file access hooks - void find_file_io_hooks() { - static constexpr uint8_t syscall[] = { - 0xed, 0xfe, 0xfe - }; - - auto begin = file_io_rom_.begin(); - while(true) { - begin = std::search( - begin, file_io_rom_.end(), - std::begin(syscall), std::end(syscall) - ); - - if(begin == file_io_rom_.end()) { - break; - } - - const auto offset = begin - file_io_rom_.begin(); - printf("Call %d at %02x\n", file_io_rom_[offset + 3], offset); - begin += 3; - } - } - // MARK: - Memory Timing // The wait mode affects all memory accesses _outside of the video area_. enum class WaitMode { @@ -793,6 +774,28 @@ private: std::unordered_set trap_locations_; bool test_host_fs_traps_ = false; + void find_host_fs_hooks() { + static constexpr uint8_t syscall[] = { + 0xed, 0xfe, 0xfe + }; + + auto begin = host_fs_rom_.begin(); + while(true) { + begin = std::search( + begin, host_fs_rom_.end(), + std::begin(syscall), std::end(syscall) + ); + + if(begin == host_fs_rom_.end()) { + break; + } + + const auto offset = begin - host_fs_rom_.begin(); + trap_locations_.insert(offset); + begin += 3; + } + } + // MARK: - Activity Source void set_activity_observer([[maybe_unused]] Activity::Observer *const observer) final { diff --git a/Machines/Enterprise/HostFS.hpp b/Machines/Enterprise/HostFS.hpp deleted file mode 100644 index 3bee0d117..000000000 --- a/Machines/Enterprise/HostFS.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// -// HostFS.h -// Clock Signal -// -// Created by Thomas Harte on 23/04/2025. -// Copyright © 2025 Thomas Harte. All rights reserved. -// - -#pragma once - -namespace Enterprise { - -constexpr uint8_t hostfs_rom[] = { - // Standard header. - 'E', 'X', 'O', 'S', '_', 'R', 'O', 'M', - - // Pointer to device chain. - 0x00, 0x00, - - // -}; - -} diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index ae33554e6..1791d5028 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1547,7 +1547,6 @@ 4B322E021F5A29D5004EB04C /* Z80Storage.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Z80Storage.hpp; sourceTree = ""; }; 4B322E031F5A2E3C004EB04C /* Z80Base.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Z80Base.cpp; sourceTree = ""; }; 4B322E051F5A30F5004EB04C /* Z80Implementation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Z80Implementation.hpp; sourceTree = ""; }; - 4B3583DB2DB965FA00A24128 /* HostFS.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = HostFS.hpp; sourceTree = ""; }; 4B37EE801D7345A6006A09A4 /* BinaryDump.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BinaryDump.cpp; sourceTree = ""; }; 4B37EE811D7345A6006A09A4 /* BinaryDump.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = BinaryDump.hpp; sourceTree = ""; }; 4B38F3461F2EC11D00D9235D /* AmstradCPC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AmstradCPC.cpp; sourceTree = ""; }; @@ -2718,12 +2717,10 @@ 4BFEA2EE2682A7B900EBF94C /* Dave.hpp */, 4B051CA02676F52200CA44E8 /* Enterprise.hpp */, 4B051CB52680158600CA44E8 /* EXDos.hpp */, - 4B3583DB2DB965FA00A24128 /* HostFS.hpp */, 4BCF1AD22ECF743500109999 /* EXOSCodes.hpp */, 4BCF1AD32ECF884100109999 /* HostFSHandler.hpp */, 4B051CAF267C1CA200CA44E8 /* Keyboard.hpp */, 4B051CAB26783E2000CA44E8 /* Nick.hpp */, - 4BCF1AD22ECF743500109999 /* EXOSCodes.hpp */, ); path = Enterprise; sourceTree = ""; From 1cf3c77ae91f36c893d31f2884b007b6b1530c9e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 20 Nov 2025 13:11:09 -0500 Subject: [PATCH 11/48] Forward file bundle and host FS traps to the handler. --- Machines/Enterprise/Enterprise.cpp | 48 ++++++++++++++++++++++++--- Machines/Enterprise/HostFSHandler.cpp | 4 +++ Machines/Enterprise/HostFSHandler.hpp | 9 +++++ 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/Machines/Enterprise/Enterprise.cpp b/Machines/Enterprise/Enterprise.cpp index 2ac2bc6df..c9c680274 100644 --- a/Machines/Enterprise/Enterprise.cpp +++ b/Machines/Enterprise/Enterprise.cpp @@ -559,6 +559,30 @@ public: case PartialMachineCycle::Read: case PartialMachineCycle::ReadOpcode: + // Potential segue for the host FS. I'm relying on branch prediction to + // avoid this cost almost always. + if(test_host_fs_traps_ && (address >> 14) == 3) [[unlikely]] { + const auto is_trap = host_fs_traps_.contains(address); + + if(is_trap) { + using Register = CPU::Z80::Register; + + uint8_t a = uint8_t(z80_.value_of(Register::A)); + uint16_t bc = z80_.value_of(Register::BC); + uint16_t de = z80_.value_of(Register::DE); + + // Grab function code from where the PC actually is, and return a NOP + host_fs_.perform(read_pointers_[address >> 14][address], a, bc, de); + *cycle.value = 0x00; // i.e. NOP. + + z80_.set_value_of(Register::A, a); + z80_.set_value_of(Register::BC, bc); + z80_.set_value_of(Register::DE, de); + + break; + } + } + if(read_pointers_[address >> 14]) { *cycle.value = read_pointers_[address >> 14][address]; } else { @@ -615,12 +639,18 @@ private: template void page(const uint8_t offset) { pages_[slot] = offset; + if constexpr (slot == 3) { + test_host_fs_traps_ = false; + } + if(page_rom(offset, 0, exos_)) return; if(page_rom(offset, 16, basic_)) return; if(page_rom(offset, 32, exdos_rom_)) return; if(page_rom(offset, 48, epdos_rom_)) return; if(page_rom(offset, 64, host_fs_rom_)) { - // TODO: test_host_fs_traps_. + if constexpr (slot == 3) { + test_host_fs_traps_ = true; + } return; } @@ -730,6 +760,10 @@ private: } } + if(!media.file_bundles.empty()) { + host_fs_.set_file_bundle(media.file_bundles.front()); + } + return true; } @@ -771,7 +805,7 @@ private: // MARK: - Host FS. HostFSHandler host_fs_; - std::unordered_set trap_locations_; + std::unordered_set host_fs_traps_; bool test_host_fs_traps_ = false; void find_host_fs_hooks() { @@ -790,9 +824,13 @@ private: break; } - const auto offset = begin - host_fs_rom_.begin(); - trap_locations_.insert(offset); - begin += 3; + const auto offset = begin - host_fs_rom_.begin() + 0xc000; // ROM will be paged in slot 3, i.e. at $c000. + host_fs_traps_.insert(offset); + + // Move function code up to where this trap was, and NOP out the tail. + begin[0] = begin[3]; + begin[1] = begin[2] = begin[3] = 0x00; + begin += 4; } } diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp index c3ed12358..b0b87fba2 100644 --- a/Machines/Enterprise/HostFSHandler.cpp +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -19,3 +19,7 @@ void HostFSHandler::perform(uint8_t function, uint8_t &a, uint16_t &bc, uint16_t (void)bc; (void)de; } + +void HostFSHandler::set_file_bundle(std::shared_ptr bundle) { + bundle_ = bundle; +} diff --git a/Machines/Enterprise/HostFSHandler.hpp b/Machines/Enterprise/HostFSHandler.hpp index affec6ac3..dfb46328b 100644 --- a/Machines/Enterprise/HostFSHandler.hpp +++ b/Machines/Enterprise/HostFSHandler.hpp @@ -8,16 +8,25 @@ #pragma once +#include "Storage/FileBundle/FileBundle.hpp" + #include namespace Enterprise { struct HostFSHandler { HostFSHandler(uint8_t *ram); + + /// Perform the internally-defined @c function given other provided state. + /// These function calls mostly align with those in EXOSCodes.hpp void perform(uint8_t function, uint8_t &a, uint16_t &bc, uint16_t &de); + /// Sets the bundle of files on which this handler should operate. + void set_file_bundle(std::shared_ptr bundle); + private: uint8_t *ram_; + std::shared_ptr bundle_; }; }; From 7bc865a2e00653812dd322b303aa2e1972b2be01 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 20 Nov 2025 13:13:32 -0500 Subject: [PATCH 12/48] Update CMake sources list. --- cmake/CLK_SOURCES.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/CLK_SOURCES.cmake b/cmake/CLK_SOURCES.cmake index 184021f8a..921f93706 100644 --- a/cmake/CLK_SOURCES.cmake +++ b/cmake/CLK_SOURCES.cmake @@ -130,6 +130,7 @@ set(CLK_SOURCES Machines/Enterprise/Dave.cpp Machines/Enterprise/EXDos.cpp Machines/Enterprise/Enterprise.cpp + Machines/Enterprise/HostFSHandler.cpp Machines/Enterprise/Keyboard.cpp Machines/Enterprise/Nick.cpp Machines/KeyboardMachine.cpp From 50cd28f88299ed9bf6f13acadab1d5a63d822dbf Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 20 Nov 2025 15:32:57 -0500 Subject: [PATCH 13/48] Add three device-descriptor-specific functions. --- Machines/Enterprise/EXOSCodes.hpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Machines/Enterprise/EXOSCodes.hpp b/Machines/Enterprise/EXOSCodes.hpp index bddb7b1b4..470db33dd 100644 --- a/Machines/Enterprise/EXOSCodes.hpp +++ b/Machines/Enterprise/EXOSCodes.hpp @@ -25,7 +25,7 @@ enum class Function: uint8_t { ReadBlock = 6, // RDBLK WriteCharacter = 7, // WRCH WriteBlock = 8, // WRBLK - ReadStatus = 9, // RSTAT + ReadChannelStatus = 9, // RSTAT SetChannelStatus = 10, // SSTAT SpecialFunction = 11, // SFUNC @@ -44,6 +44,16 @@ enum class Function: uint8_t { ReturnErrorMessage = 28, // ERRMSG }; +// Page 25. +enum class DeviceDescriptorFunction: uint8_t { + // + // Codes are the same as `Function` in the range 1–11. + // + Interrupt = 0, + Initialise = 12, + BufferMoved = 13, +}; + // Pages 68–70. enum class Error: uint8_t { // From 923fdd42ec4263e3824eabe5dfd316a569a7ed60 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 20 Nov 2025 15:35:06 -0500 Subject: [PATCH 14/48] Add a printf for tracking. --- Machines/Enterprise/HostFSHandler.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp index b0b87fba2..74c3aebfa 100644 --- a/Machines/Enterprise/HostFSHandler.cpp +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -13,8 +13,13 @@ using namespace Enterprise; HostFSHandler::HostFSHandler(uint8_t *ram) : ram_(ram) {} -void HostFSHandler::perform(uint8_t function, uint8_t &a, uint16_t &bc, uint16_t &de) { - (void)function; +void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, uint16_t &de) { + switch(function) { + default: + printf("UNIMPLEMENTED function %d\n", function); + break; + } + (void)a; (void)bc; (void)de; From 9ee74256279ee8c9bcead7d7508c07b9bc149760 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 20 Nov 2025 15:58:53 -0500 Subject: [PATCH 15/48] Add missing include for std::shared_ptr. --- Machines/Enterprise/HostFSHandler.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Machines/Enterprise/HostFSHandler.hpp b/Machines/Enterprise/HostFSHandler.hpp index dfb46328b..456232877 100644 --- a/Machines/Enterprise/HostFSHandler.hpp +++ b/Machines/Enterprise/HostFSHandler.hpp @@ -11,6 +11,7 @@ #include "Storage/FileBundle/FileBundle.hpp" #include +#include namespace Enterprise { From 39a96a7f73f6dc25fe72daf5d9a2c11ec9d5f5d4 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 20 Nov 2025 16:15:04 -0500 Subject: [PATCH 16/48] Proceed to realisation that I'm probably looking in the wrong RAM. --- Machines/Enterprise/EXOSCodes.hpp | 2 ++ Machines/Enterprise/HostFSHandler.cpp | 32 +++++++++++++++++++++++++-- Machines/Enterprise/HostFSHandler.hpp | 4 ++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/Machines/Enterprise/EXOSCodes.hpp b/Machines/Enterprise/EXOSCodes.hpp index 470db33dd..e66b59951 100644 --- a/Machines/Enterprise/EXOSCodes.hpp +++ b/Machines/Enterprise/EXOSCodes.hpp @@ -56,6 +56,8 @@ enum class DeviceDescriptorFunction: uint8_t { // Pages 68–70. enum class Error: uint8_t { + NoError = 0x00, + // // General errors returned by the EXOS kernel. // diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp index 74c3aebfa..dab92df1a 100644 --- a/Machines/Enterprise/HostFSHandler.cpp +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -14,15 +14,43 @@ using namespace Enterprise; HostFSHandler::HostFSHandler(uint8_t *ram) : ram_(ram) {} void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, uint16_t &de) { + const auto set_error = [&](const EXOS::Error error) { + a = uint8_t(error); + }; + const auto read_de = [&]() { + return ram_[de++]; + }; + switch(function) { default: printf("UNIMPLEMENTED function %d\n", function); break; + + case uint8_t(EXOS::DeviceDescriptorFunction::Initialise): + channels_.clear(); + set_error(EXOS::Error::NoError); + break; + + // Page 54. + case uint8_t(EXOS::Function::OpenChannel): + case uint8_t(EXOS::Function::CreateChannel): { + if(a == 255) { + set_error(EXOS::Error::ChannelIllegalOrDoesNotExist); + break; + } + + // Get name. + uint8_t length = read_de(); + std::string name; + while(length--) { + name.push_back(char(read_de())); + } + + printf("Should open %s\n", name.c_str()); + } break; } - (void)a; (void)bc; - (void)de; } void HostFSHandler::set_file_bundle(std::shared_ptr bundle) { diff --git a/Machines/Enterprise/HostFSHandler.hpp b/Machines/Enterprise/HostFSHandler.hpp index 456232877..a07ed15a5 100644 --- a/Machines/Enterprise/HostFSHandler.hpp +++ b/Machines/Enterprise/HostFSHandler.hpp @@ -12,6 +12,7 @@ #include #include +#include namespace Enterprise { @@ -28,6 +29,9 @@ struct HostFSHandler { private: uint8_t *ram_; std::shared_ptr bundle_; + + using ChannelHandler = uint8_t; + std::unordered_map channels_; }; }; From 4a93264dc5ff1a99aa46114536f57eaf999785da Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 20 Nov 2025 17:12:15 -0500 Subject: [PATCH 17/48] Add move semantics. --- Storage/FileHolder.cpp | 6 ++++++ Storage/FileHolder.hpp | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Storage/FileHolder.cpp b/Storage/FileHolder.cpp index 850ecf96a..70c16b719 100644 --- a/Storage/FileHolder.cpp +++ b/Storage/FileHolder.cpp @@ -13,6 +13,12 @@ using namespace Storage; +FileHolder::FileHolder(FileHolder &&rhs) { + file_ = rhs.file_; + rhs.file_ = nullptr; + // TODO: this leaves the RHS in an invalid state, which isn't appropriate for move semantics. +} + FileHolder::~FileHolder() { if(file_) std::fclose(file_); } diff --git a/Storage/FileHolder.hpp b/Storage/FileHolder.hpp index 9d101600e..0accb260c 100644 --- a/Storage/FileHolder.hpp +++ b/Storage/FileHolder.hpp @@ -56,9 +56,10 @@ public: Rewrite opens the file for rewriting; none of the original content is preserved; whatever the caller outputs will replace the existing file. - @throws ErrorCantOpen if the file cannot be opened. + @throws Error::CantOpen if the file cannot be opened. */ FileHolder(const std::string &file_name, FileMode ideal_mode = FileMode::ReadWrite); + FileHolder(FileHolder &&); /*! Writes @c value using successive @c puts, in little endian order. From 314154e9fd942cd850ee30ab7c8b310be1a0edcd Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 20 Nov 2025 17:12:31 -0500 Subject: [PATCH 18/48] Implement open/create and close, of the key file. --- Analyser/Static/Enterprise/StaticAnalyser.cpp | 18 +++++---- Machines/Enterprise/Enterprise.cpp | 20 +++++++++- Machines/Enterprise/HostFSHandler.cpp | 38 +++++++++++++++++-- Machines/Enterprise/HostFSHandler.hpp | 9 ++++- Storage/FileBundle/FileBundle.cpp | 13 +++++++ Storage/FileBundle/FileBundle.hpp | 14 +++---- 6 files changed, 89 insertions(+), 23 deletions(-) diff --git a/Analyser/Static/Enterprise/StaticAnalyser.cpp b/Analyser/Static/Enterprise/StaticAnalyser.cpp index a9e2ef43b..e07a07a20 100644 --- a/Analyser/Static/Enterprise/StaticAnalyser.cpp +++ b/Analyser/Static/Enterprise/StaticAnalyser.cpp @@ -89,15 +89,19 @@ Analyser::Static::TargetList Analyser::Static::Enterprise::GetTargets( if(!media.file_bundles.empty()) { auto file = media.file_bundles.front()->key_file(); - // Check for a .COM by inspecting the header. - const uint16_t type = file.get_le(); - const uint16_t size = file.get_le(); - // To consider: all the COMs I've seen also now have 12 bytes of 0 padding. - // Test that? + if(file.has_value()) { + // Check for a .COM by inspecting the header. + const uint16_t type = file->get_le(); + const uint16_t size = file->get_le(); + // To consider: all the COMs I've seen also now have 12 bytes of 0 padding. + // Test that? - if(type != 0x0500 || size != file.stats().st_size - 16) { - target->media.file_bundles.clear(); + if(type != 0x0500 || size != file->stats().st_size - 16) { + target->media.file_bundles.clear(); + } } + + // TODO: look for a key file, similar logic to above. } if(!target->media.empty()) { diff --git a/Machines/Enterprise/Enterprise.cpp b/Machines/Enterprise/Enterprise.cpp index c9c680274..8ddbad673 100644 --- a/Machines/Enterprise/Enterprise.cpp +++ b/Machines/Enterprise/Enterprise.cpp @@ -75,6 +75,7 @@ template class ConcreteMachine: public Activity::Source, public Configurable::Device, public CPU::Z80::BusHandler, + public HostFSHandler::MemoryAccessor, public Machine, public MachineTypes::AudioProducer, public MachineTypes::MappedKeyboardMachine, @@ -105,7 +106,7 @@ public: ConcreteMachine(const Analyser::Static::Enterprise::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : min_ram_slot_(min_ram_slot(target)), z80_(*this), - host_fs_(ram_.data()), // TODO: is this the correct section? + host_fs_(*this), nick_(ram_.end() - 65536), dave_audio_(audio_queue_), speaker_(dave_audio_) { @@ -557,7 +558,6 @@ public: } break; - case PartialMachineCycle::Read: case PartialMachineCycle::ReadOpcode: // Potential segue for the host FS. I'm relying on branch prediction to // avoid this cost almost always. @@ -582,7 +582,9 @@ public: break; } } + [[fallthrough]]; + case PartialMachineCycle::Read: if(read_pointers_[address >> 14]) { *cycle.value = read_pointers_[address >> 14][address]; } else { @@ -808,6 +810,20 @@ private: std::unordered_set host_fs_traps_; bool test_host_fs_traps_ = false; + uint8_t hostfs_read(const uint16_t address) override { + if(read_pointers_[address >> 14]) { + return read_pointers_[address >> 14][address]; + } else { + return 0xff; + } + } + + void hostfs_write(const uint16_t address, const uint8_t value) override { + if(write_pointers_[address >> 14]) { + write_pointers_[address >> 14][address] = value; + } + } + void find_host_fs_hooks() { static constexpr uint8_t syscall[] = { 0xed, 0xfe, 0xfe diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp index dab92df1a..9f860282e 100644 --- a/Machines/Enterprise/HostFSHandler.cpp +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -11,14 +11,14 @@ using namespace Enterprise; -HostFSHandler::HostFSHandler(uint8_t *ram) : ram_(ram) {} +HostFSHandler::HostFSHandler(MemoryAccessor &accessor) : accessor_(accessor) {} void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, uint16_t &de) { const auto set_error = [&](const EXOS::Error error) { a = uint8_t(error); }; const auto read_de = [&]() { - return ram_[de++]; + return accessor_.hostfs_read(de++); }; switch(function) { @@ -32,6 +32,7 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui break; // Page 54. + // Emprically: C contains the unit number. case uint8_t(EXOS::Function::OpenChannel): case uint8_t(EXOS::Function::CreateChannel): { if(a == 255) { @@ -46,7 +47,38 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui name.push_back(char(read_de())); } - printf("Should open %s\n", name.c_str()); + // The only difference between open and create is that the former is + // meant to append. + const auto mode = + function == uint8_t(EXOS::Function::CreateChannel) ? + Storage::FileMode::Rewrite : Storage::FileMode::ReadWrite; + + set_error(EXOS::Error::NoError); + try { + channels_.emplace(a, bundle_->open(name, mode)); + } catch(Storage::FileHolder::Error) { + if(mode == Storage::FileMode::ReadWrite) { + try { + channels_.emplace(a, bundle_->open(name, Storage::FileMode::Read)); + } catch(Storage::FileHolder::Error) { + set_error(EXOS::Error::FileDoesNotExist); + } + } else { + set_error(EXOS::Error::FileAlreadyExists); + } + } + } break; + + // Page 54. + case uint8_t(EXOS::Function::CloseChannel): { + auto channel = channels_.find(a); + if(a == 255 || channel == channels_.end()) { + set_error(EXOS::Error::ChannelIllegalOrDoesNotExist); + break; + } + + set_error(EXOS::Error::NoError); + channels_.erase(channel); } break; } diff --git a/Machines/Enterprise/HostFSHandler.hpp b/Machines/Enterprise/HostFSHandler.hpp index a07ed15a5..678b3baf1 100644 --- a/Machines/Enterprise/HostFSHandler.hpp +++ b/Machines/Enterprise/HostFSHandler.hpp @@ -17,7 +17,12 @@ namespace Enterprise { struct HostFSHandler { - HostFSHandler(uint8_t *ram); + struct MemoryAccessor { + virtual uint8_t hostfs_read(uint16_t) = 0; + virtual void hostfs_write(uint16_t, uint8_t) = 0; + }; + + HostFSHandler(MemoryAccessor &); /// Perform the internally-defined @c function given other provided state. /// These function calls mostly align with those in EXOSCodes.hpp @@ -27,7 +32,7 @@ struct HostFSHandler { void set_file_bundle(std::shared_ptr bundle); private: - uint8_t *ram_; + MemoryAccessor &accessor_; std::shared_ptr bundle_; using ChannelHandler = uint8_t; diff --git a/Storage/FileBundle/FileBundle.cpp b/Storage/FileBundle/FileBundle.cpp index 93fcec767..3791bbfeb 100644 --- a/Storage/FileBundle/FileBundle.cpp +++ b/Storage/FileBundle/FileBundle.cpp @@ -7,3 +7,16 @@ // #include "FileBundle.hpp" + +using namespace Storage::FileBundle; + +std::optional LocalFSFileBundle::key_file() { + return {to_contain_}; +} + +Storage::FileHolder LocalFSFileBundle::open(const std::string &name, Storage::FileMode mode) { + // TODO: append path. Just cheat for now. + (void)name; + return {/* name */ to_contain_, mode}; +} + diff --git a/Storage/FileBundle/FileBundle.hpp b/Storage/FileBundle/FileBundle.hpp index 60d64c7d8..680c01637 100644 --- a/Storage/FileBundle/FileBundle.hpp +++ b/Storage/FileBundle/FileBundle.hpp @@ -9,6 +9,7 @@ #pragma once #include "Storage/FileHolder.hpp" +#include #include namespace Storage::FileBundle { @@ -22,21 +23,16 @@ namespace Storage::FileBundle { bundles in the future. */ struct FileBundle { - virtual FileHolder key_file() = 0; - virtual uint32_t open(const std::string &) = 0; + virtual std::optional key_file() = 0; + virtual FileHolder open(const std::string &, FileMode) = 0; }; struct LocalFSFileBundle: public FileBundle { LocalFSFileBundle(const std::string &to_contain) : to_contain_(to_contain) {} - FileHolder key_file() override { - return FileHolder(to_contain_); - } - - uint32_t open(const std::string &) override { - return 0; - } + std::optional key_file() override; + FileHolder open(const std::string &, FileMode) override; private: std::string to_contain_; From 51eea4dea3496f1e9f6933004abf8add8fb638a8 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 20 Nov 2025 17:16:30 -0500 Subject: [PATCH 19/48] Attempt read character. --- Machines/Enterprise/HostFSHandler.cpp | 28 +++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp index 9f860282e..7d65cfef8 100644 --- a/Machines/Enterprise/HostFSHandler.cpp +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -17,9 +17,19 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui const auto set_error = [&](const EXOS::Error error) { a = uint8_t(error); }; + const auto set_c = [&](const uint8_t c) { + bc = (bc & 0xff00) | c; + }; + const auto read_de = [&]() { return accessor_.hostfs_read(de++); }; + const auto find_channel = [&]() { + if(a == 255) { + return channels_.end(); + } + return channels_.find(a); + }; switch(function) { default: @@ -71,8 +81,8 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui // Page 54. case uint8_t(EXOS::Function::CloseChannel): { - auto channel = channels_.find(a); - if(a == 255 || channel == channels_.end()) { + auto channel = find_channel(); + if(channel == channels_.end()) { set_error(EXOS::Error::ChannelIllegalOrDoesNotExist); break; } @@ -80,9 +90,19 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui set_error(EXOS::Error::NoError); channels_.erase(channel); } break; - } - (void)bc; + // Page 55. + case uint8_t(EXOS::Function::ReadCharacter): + auto channel = find_channel(); + if(channel == channels_.end()) { + set_error(EXOS::Error::ChannelIllegalOrDoesNotExist); + break; + } + + set_error(EXOS::Error::NoError); + set_c(channel->second.get()); + break; + } } void HostFSHandler::set_file_bundle(std::shared_ptr bundle) { From 3196840b05705feb09a7c83e89879c729bc092f6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 20 Nov 2025 17:26:44 -0500 Subject: [PATCH 20/48] Avoiding losing channel number; implement EOF check. --- Machines/Enterprise/HostFSHandler.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp index 7d65cfef8..4ad2cf34d 100644 --- a/Machines/Enterprise/HostFSHandler.cpp +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -63,13 +63,14 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui function == uint8_t(EXOS::Function::CreateChannel) ? Storage::FileMode::Rewrite : Storage::FileMode::ReadWrite; - set_error(EXOS::Error::NoError); try { channels_.emplace(a, bundle_->open(name, mode)); + set_error(EXOS::Error::NoError); } catch(Storage::FileHolder::Error) { if(mode == Storage::FileMode::ReadWrite) { try { channels_.emplace(a, bundle_->open(name, Storage::FileMode::Read)); + set_error(EXOS::Error::NoError); } catch(Storage::FileHolder::Error) { set_error(EXOS::Error::FileDoesNotExist); } @@ -77,6 +78,8 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui set_error(EXOS::Error::FileAlreadyExists); } } + + // TODO: FileAlreadyOpen, FileAlreadyExists. } break; // Page 54. @@ -99,8 +102,13 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui break; } - set_error(EXOS::Error::NoError); - set_c(channel->second.get()); + const auto next = channel->second.get(); + if(!channel->second.eof()) { + set_error(EXOS::Error::NoError); + set_c(next); + } else { + set_error(EXOS::Error::EndOfFileMetInRead); + } break; } } From fc5d93f9cc61798e07457f00899994205536ecb8 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 20 Nov 2025 20:08:00 -0500 Subject: [PATCH 21/48] Focus on writing an in-machine ROM. --- Machines/Enterprise/HostFS/hostfs.z80s | 26 ++++++++++++++++++++++++-- Machines/Enterprise/HostFSHandler.cpp | 2 +- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Machines/Enterprise/HostFS/hostfs.z80s b/Machines/Enterprise/HostFS/hostfs.z80s index 5ed1fc38b..c14472141 100644 --- a/Machines/Enterprise/HostFS/hostfs.z80s +++ b/Machines/Enterprise/HostFS/hostfs.z80s @@ -3,10 +3,32 @@ ; E.g. pyz80 --obj=hostfs.rom hostfs.z80s ; + org $c000 + dm "EXOS_ROM" ; Standard ROM signature. - dw device_chain ; Pointer to the included device chain. + + ; Pointer to the included device chain, which should be valid when this + ; ROM is paged at $4000, though when executed from it'll be at $c000. + dw $4000 + (device_chain & $3fff) ; Startup entry point; there is no startup code. dw 0 -device_chain: \ No newline at end of file + dw 0 ; XX_NEXT_LOW/HI: Pointer to start of next device. There is no next device. + dw 0 ; XX_RAM_LOW/HI: Amount of host RAM used. + +device_chain_type: + db 0 ; DD_TYPE: Type, which must be 0. + db 0 ; DD_IRQFLAG: No interrupts required. + db 0 ; DD_FLAGS: Not a video device. + dw $4000 + (dispatch & $3fff) + db 0 ; DD_TAB_LOW/HI/SEG: + db 1 ; DD_UNIT_COUNT + db 4 + dm "FILE" ; DD_NAME + +device_chain: + dw device_chain - device_chain_type + +dispatch: + dw 23 \ No newline at end of file diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp index 4ad2cf34d..f3f19fc7b 100644 --- a/Machines/Enterprise/HostFSHandler.cpp +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -33,7 +33,7 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui switch(function) { default: - printf("UNIMPLEMENTED function %d\n", function); + printf("UNIMPLEMENTED function %d with A:%02x BC:%04x DE:%04x\n", function, a, bc, de); break; case uint8_t(EXOS::DeviceDescriptorFunction::Initialise): From 6d71ad9bcc36a9ef70c43d3956c9afb7103030e1 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 20 Nov 2025 20:11:20 -0500 Subject: [PATCH 22/48] I think this is code, not a pointer. --- Machines/Enterprise/HostFS/compile.sh | 1 + Machines/Enterprise/HostFS/hostfs.z80s | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 Machines/Enterprise/HostFS/compile.sh diff --git a/Machines/Enterprise/HostFS/compile.sh b/Machines/Enterprise/HostFS/compile.sh new file mode 100644 index 000000000..9339a9564 --- /dev/null +++ b/Machines/Enterprise/HostFS/compile.sh @@ -0,0 +1 @@ +pyz80 --obj=hostfs.rom hostfs.z80s diff --git a/Machines/Enterprise/HostFS/hostfs.z80s b/Machines/Enterprise/HostFS/hostfs.z80s index c14472141..90b1ecdf3 100644 --- a/Machines/Enterprise/HostFS/hostfs.z80s +++ b/Machines/Enterprise/HostFS/hostfs.z80s @@ -3,16 +3,16 @@ ; E.g. pyz80 --obj=hostfs.rom hostfs.z80s ; - org $c000 + org 0xc000 dm "EXOS_ROM" ; Standard ROM signature. ; Pointer to the included device chain, which should be valid when this ; ROM is paged at $4000, though when executed from it'll be at $c000. - dw $4000 + (device_chain & $3fff) + dw 0x4000 + (device_chain & 0x3fff) - ; Startup entry point; there is no startup code. - dw 0 + ; ROM entry point; there is no startup code. + ret dw 0 ; XX_NEXT_LOW/HI: Pointer to start of next device. There is no next device. dw 0 ; XX_RAM_LOW/HI: Amount of host RAM used. @@ -21,7 +21,7 @@ device_chain_type: db 0 ; DD_TYPE: Type, which must be 0. db 0 ; DD_IRQFLAG: No interrupts required. db 0 ; DD_FLAGS: Not a video device. - dw $4000 + (dispatch & $3fff) + dw 0x4000 + (dispatch & 0x3fff) db 0 ; DD_TAB_LOW/HI/SEG: db 1 ; DD_UNIT_COUNT db 4 From 6dacc50163cf4feeaa67471ed499cdef44d4e29b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 20 Nov 2025 21:50:57 -0500 Subject: [PATCH 23/48] Add dispatch table and hooks. --- Machines/Enterprise/HostFS/hostfs.z80s | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Machines/Enterprise/HostFS/hostfs.z80s b/Machines/Enterprise/HostFS/hostfs.z80s index 90b1ecdf3..f8667fc0e 100644 --- a/Machines/Enterprise/HostFS/hostfs.z80s +++ b/Machines/Enterprise/HostFS/hostfs.z80s @@ -23,7 +23,7 @@ device_chain_type: db 0 ; DD_FLAGS: Not a video device. dw 0x4000 + (dispatch & 0x3fff) db 0 ; DD_TAB_LOW/HI/SEG: - db 1 ; DD_UNIT_COUNT + db 0 ; DD_UNIT_COUNT: ? db 4 dm "FILE" ; DD_NAME @@ -31,4 +31,12 @@ device_chain: dw device_chain - device_chain_type dispatch: - dw 23 \ No newline at end of file + @dispatch: EQU FOR 14 + dw call{@dispatch} + NEXT @dispatch + +@call: EQU FOR 14 +call{@call}: + db 0xed, 0xfe, 0xfe + db @call +NEXT @call From 16e41444090b35299681b1c1cee6a6db3f31cf2f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 20 Nov 2025 22:30:15 -0500 Subject: [PATCH 24/48] Switch to local ROM. --- Machines/Enterprise/Enterprise.cpp | 20 ++++++++++++-------- Machines/Enterprise/HostFS/hostfs.z80s | 14 +++++++++++++- Machines/Enterprise/HostFSHandler.cpp | 14 ++++++++++++++ Machines/Enterprise/HostFSHandler.hpp | 3 +++ 4 files changed, 42 insertions(+), 9 deletions(-) diff --git a/Machines/Enterprise/Enterprise.cpp b/Machines/Enterprise/Enterprise.cpp index 8ddbad673..0854bdeb0 100644 --- a/Machines/Enterprise/Enterprise.cpp +++ b/Machines/Enterprise/Enterprise.cpp @@ -181,9 +181,9 @@ public: // Ensure a briddge into the local filing system if requested. const bool needs_host_bridge = !target.media.file_bundles.empty(); - if(needs_host_bridge) { - request = request && ROM::Request(ROM::Name::EnterpriseEPFILEIO); - } +// if(needs_host_bridge) { +// request = request && ROM::Request(ROM::Name::EnterpriseEPFILEIO); +// } // Get and validate ROMs. auto roms = rom_fetcher(request); @@ -240,10 +240,12 @@ public: } // Possibly grab the file IO ROM. - const auto host_fs = roms.find(ROM::Name::EnterpriseEPFILEIO); - host_fs_rom_.fill(0xff); - if(host_fs != roms.end()) { - memcpy(host_fs_rom_.data(), host_fs->second.data(), std::min(host_fs_rom_.size(), host_fs->second.size())); +// const auto host_fs = roms.find(ROM::Name::EnterpriseEPFILEIO); +// host_fs_rom_.fill(0xff); + if(needs_host_bridge) {//host_fs != roms.end()) { +// memcpy(host_fs_rom_.data(), host_fs->second.data(), std::min(host_fs_rom_.size(), host_fs->second.size())); + const auto rom = host_fs_.rom(); + std::copy(rom.begin(), rom.end(), host_fs_rom_.begin()); find_host_fs_hooks(); } @@ -562,6 +564,8 @@ public: // Potential segue for the host FS. I'm relying on branch prediction to // avoid this cost almost always. if(test_host_fs_traps_ && (address >> 14) == 3) [[unlikely]] { + printf("%04x\n", address); + const auto is_trap = host_fs_traps_.contains(address); if(is_trap) { @@ -573,7 +577,7 @@ public: // Grab function code from where the PC actually is, and return a NOP host_fs_.perform(read_pointers_[address >> 14][address], a, bc, de); - *cycle.value = 0x00; // i.e. NOP. + *cycle.value = 0xc9; // i.e. RET. z80_.set_value_of(Register::A, a); z80_.set_value_of(Register::BC, bc); diff --git a/Machines/Enterprise/HostFS/hostfs.z80s b/Machines/Enterprise/HostFS/hostfs.z80s index f8667fc0e..bb591f324 100644 --- a/Machines/Enterprise/HostFS/hostfs.z80s +++ b/Machines/Enterprise/HostFS/hostfs.z80s @@ -1,6 +1,16 @@ ; ; Designed for assembly with pyz80, https://github.com/simonowen/pyz80/ ; E.g. pyz80 --obj=hostfs.rom hostfs.z80s +; + +; +; Sources: +; +; http://ep.homeserver.hu/Dokumentacio/Konyvek/EXOS_2.1_technikal_information/exos/kernel/Ch9.html +; on signature, device chain pointer and ROM entry point +; +; http://ep.homeserver.hu/Dokumentacio/Konyvek/EXOS_2.1_technikal_information/exos/kernel/Ch6.html +; on the device chain ; org 0xc000 @@ -15,14 +25,16 @@ ret dw 0 ; XX_NEXT_LOW/HI: Pointer to start of next device. There is no next device. - dw 0 ; XX_RAM_LOW/HI: Amount of host RAM used. + dw 0xfffe ; XX_RAM_LOW/HI: [(Amount of host RAM used) + 2] negatived. device_chain_type: db 0 ; DD_TYPE: Type, which must be 0. db 0 ; DD_IRQFLAG: No interrupts required. db 0 ; DD_FLAGS: Not a video device. + dw 0x4000 + (dispatch & 0x3fff) db 0 ; DD_TAB_LOW/HI/SEG: + db 0 ; DD_UNIT_COUNT: ? db 4 dm "FILE" ; DD_NAME diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp index f3f19fc7b..c031dbbd7 100644 --- a/Machines/Enterprise/HostFSHandler.cpp +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -116,3 +116,17 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui void HostFSHandler::set_file_bundle(std::shared_ptr bundle) { bundle_ = bundle; } + +std::vector HostFSHandler::rom() { + // Assembled and transcribed from hostfs.z80. + return std::vector{ + 0x45, 0x58, 0x4f, 0x53, 0x5f, 0x52, 0x4f, 0x4d, 0x1b, 0x40, 0xc9, 0x00, 0x00, 0xfe, 0xff, 0x00, + 0x00, 0x00, 0x1d, 0x40, 0x00, 0x00, 0x04, 0x46, 0x49, 0x4c, 0x45, 0x0c, 0x00, 0x39, 0xc0, 0x3d, + 0xc0, 0x41, 0xc0, 0x45, 0xc0, 0x49, 0xc0, 0x4d, 0xc0, 0x51, 0xc0, 0x55, 0xc0, 0x59, 0xc0, 0x5d, + 0xc0, 0x61, 0xc0, 0x65, 0xc0, 0x69, 0xc0, 0x6d, 0xc0, 0xed, 0xfe, 0xfe, 0x00, 0xed, 0xfe, 0xfe, + 0x01, 0xed, 0xfe, 0xfe, 0x02, 0xed, 0xfe, 0xfe, 0x03, 0xed, 0xfe, 0xfe, 0x04, 0xed, 0xfe, 0xfe, + 0x05, 0xed, 0xfe, 0xfe, 0x06, 0xed, 0xfe, 0xfe, 0x07, 0xed, 0xfe, 0xfe, 0x08, 0xed, 0xfe, 0xfe, + 0x09, 0xed, 0xfe, 0xfe, 0x0a, 0xed, 0xfe, 0xfe, 0x0b, 0xed, 0xfe, 0xfe, 0x0c, 0xed, 0xfe, 0xfe, + 0x0d + }; +} diff --git a/Machines/Enterprise/HostFSHandler.hpp b/Machines/Enterprise/HostFSHandler.hpp index 678b3baf1..8ebf6e7d9 100644 --- a/Machines/Enterprise/HostFSHandler.hpp +++ b/Machines/Enterprise/HostFSHandler.hpp @@ -13,6 +13,7 @@ #include #include #include +#include namespace Enterprise { @@ -31,6 +32,8 @@ struct HostFSHandler { /// Sets the bundle of files on which this handler should operate. void set_file_bundle(std::shared_ptr bundle); + std::vector rom(); + private: MemoryAccessor &accessor_; std::shared_ptr bundle_; From 6e8e2b62017696499020b80da33bb52f224a3ba0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 21 Nov 2025 12:32:06 -0500 Subject: [PATCH 25/48] Call EXOS for buffer allocation. --- Machines/Enterprise/Enterprise.cpp | 4 +- Machines/Enterprise/HostFS/hostfs.z80s | 137 ++++++++++++++++++++++++- Machines/Enterprise/HostFSHandler.cpp | 16 +-- 3 files changed, 142 insertions(+), 15 deletions(-) diff --git a/Machines/Enterprise/Enterprise.cpp b/Machines/Enterprise/Enterprise.cpp index 0854bdeb0..2b0dd624d 100644 --- a/Machines/Enterprise/Enterprise.cpp +++ b/Machines/Enterprise/Enterprise.cpp @@ -564,8 +564,6 @@ public: // Potential segue for the host FS. I'm relying on branch prediction to // avoid this cost almost always. if(test_host_fs_traps_ && (address >> 14) == 3) [[unlikely]] { - printf("%04x\n", address); - const auto is_trap = host_fs_traps_.contains(address); if(is_trap) { @@ -577,7 +575,7 @@ public: // Grab function code from where the PC actually is, and return a NOP host_fs_.perform(read_pointers_[address >> 14][address], a, bc, de); - *cycle.value = 0xc9; // i.e. RET. + *cycle.value = 0x00; // i.e. NOP. z80_.set_value_of(Register::A, a); z80_.set_value_of(Register::BC, bc); diff --git a/Machines/Enterprise/HostFS/hostfs.z80s b/Machines/Enterprise/HostFS/hostfs.z80s index bb591f324..e53ae5166 100644 --- a/Machines/Enterprise/HostFS/hostfs.z80s +++ b/Machines/Enterprise/HostFS/hostfs.z80s @@ -13,6 +13,11 @@ ; on the device chain ; + hostfscall: macro + db 0xed, 0xfe, 0xfe + db \0 + endm + org 0xc000 dm "EXOS_ROM" ; Standard ROM signature. @@ -47,8 +52,130 @@ dispatch: dw call{@dispatch} NEXT @dispatch -@call: EQU FOR 14 -call{@call}: - db 0xed, 0xfe, 0xfe - db @call -NEXT @call +; +; Interrupt. +; +; The device chain indicates that this ROM doesn't receive interrupts. So no need to escalate. +; +call0: + ret + +; +; Open channel. +; +; EXOS requires the programmer manually to call its function 27 to allocate a channel buffer if +; it otherwise expects to succeed. So some handling is most easily done within the client machine. +; +call1: + ld b, a ; Backup the channel number + hostfscall 1 + +allocate_exos_buffer: + ; Exit immediately if that call already failed. + and a + ret nz + + ; Restore the channel number and otherwise configure to allocate a buffer. + push bc + ld a, b + ld bc, 0 + ld de, 1 + + ; EXOS call 27. + rst 0x30 + db 27 + + ; If there's no error from that, exit. + pop bc + and a + ret z + + ; Otherwise, close the file again. What fun! + ld a, b + hostfscall 3 + ret + +; +; Create channel. +; +call2: + ld b, a + hostfscall 2 + jp allocate_exos_buffer + +; +; Close channel. +; +call3: + hostfscall 3 + ret + +; +; Destroy channel. +; +call4: + hostfscall 4 + ret + +; +; Read character. +; +call5: + hostfscall 5 + ret + +; +; Read block. +; +call6: + hostfscall 6 + ret + +; +; Write character. +; +call7: + hostfscall 7 + ret + +; +; Write block. +; +call8: + hostfscall 8 + ret + +; +; Read channel status. +; +call9: + hostfscall 9 + ret + +; +; Set channel status. +; +call10: + hostfscall 10 + ret + +; +; Special function. +; +call11: + hostfscall 11 + ret + +; +; Initialise. +; +call12: + hostfscall 12 + ret + +; +; Buffer moved. +; +call13: + hostfscall 13 + ret diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp index c031dbbd7..cb9d310a3 100644 --- a/Machines/Enterprise/HostFSHandler.cpp +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -121,12 +121,14 @@ std::vector HostFSHandler::rom() { // Assembled and transcribed from hostfs.z80. return std::vector{ 0x45, 0x58, 0x4f, 0x53, 0x5f, 0x52, 0x4f, 0x4d, 0x1b, 0x40, 0xc9, 0x00, 0x00, 0xfe, 0xff, 0x00, - 0x00, 0x00, 0x1d, 0x40, 0x00, 0x00, 0x04, 0x46, 0x49, 0x4c, 0x45, 0x0c, 0x00, 0x39, 0xc0, 0x3d, - 0xc0, 0x41, 0xc0, 0x45, 0xc0, 0x49, 0xc0, 0x4d, 0xc0, 0x51, 0xc0, 0x55, 0xc0, 0x59, 0xc0, 0x5d, - 0xc0, 0x61, 0xc0, 0x65, 0xc0, 0x69, 0xc0, 0x6d, 0xc0, 0xed, 0xfe, 0xfe, 0x00, 0xed, 0xfe, 0xfe, - 0x01, 0xed, 0xfe, 0xfe, 0x02, 0xed, 0xfe, 0xfe, 0x03, 0xed, 0xfe, 0xfe, 0x04, 0xed, 0xfe, 0xfe, - 0x05, 0xed, 0xfe, 0xfe, 0x06, 0xed, 0xfe, 0xfe, 0x07, 0xed, 0xfe, 0xfe, 0x08, 0xed, 0xfe, 0xfe, - 0x09, 0xed, 0xfe, 0xfe, 0x0a, 0xed, 0xfe, 0xfe, 0x0b, 0xed, 0xfe, 0xfe, 0x0c, 0xed, 0xfe, 0xfe, - 0x0d + 0x00, 0x00, 0x1d, 0x40, 0x00, 0x00, 0x04, 0x46, 0x49, 0x4c, 0x45, 0x0c, 0x00, 0x39, 0xc0, 0x3a, + 0xc0, 0x54, 0xc0, 0x5c, 0xc0, 0x61, 0xc0, 0x66, 0xc0, 0x6b, 0xc0, 0x70, 0xc0, 0x75, 0xc0, 0x7a, + 0xc0, 0x7f, 0xc0, 0x84, 0xc0, 0x89, 0xc0, 0x8e, 0xc0, 0xc9, 0x47, 0xed, 0xfe, 0xfe, 0x01, 0xa7, + 0xc0, 0xc5, 0x78, 0x01, 0x00, 0x00, 0x11, 0x01, 0x00, 0xf7, 0x1b, 0xc1, 0xa7, 0xc8, 0x78, 0xed, + 0xfe, 0xfe, 0x03, 0xc9, 0x47, 0xed, 0xfe, 0xfe, 0x02, 0xc3, 0x3f, 0xc0, 0xed, 0xfe, 0xfe, 0x03, + 0xc9, 0xed, 0xfe, 0xfe, 0x04, 0xc9, 0xed, 0xfe, 0xfe, 0x05, 0xc9, 0xed, 0xfe, 0xfe, 0x06, 0xc9, + 0xed, 0xfe, 0xfe, 0x07, 0xc9, 0xed, 0xfe, 0xfe, 0x08, 0xc9, 0xed, 0xfe, 0xfe, 0x09, 0xc9, 0xed, + 0xfe, 0xfe, 0x0a, 0xc9, 0xed, 0xfe, 0xfe, 0x0b, 0xc9, 0xed, 0xfe, 0xfe, 0x0c, 0xc9, 0xed, 0xfe, + 0xfe, 0x0d, 0xc9 }; } From 3d192e22f2d3b4d1730b17c9718482639a9e7121 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 21 Nov 2025 12:58:54 -0500 Subject: [PATCH 26/48] It seems error codes changed at some point. --- Analyser/Static/Enterprise/StaticAnalyser.cpp | 2 + Machines/Enterprise/EXOSCodes.hpp | 159 +++++++++--------- Machines/Enterprise/HostFSHandler.cpp | 8 +- Machines/Enterprise/HostFSHandler.hpp | 1 + 4 files changed, 90 insertions(+), 80 deletions(-) diff --git a/Analyser/Static/Enterprise/StaticAnalyser.cpp b/Analyser/Static/Enterprise/StaticAnalyser.cpp index e07a07a20..aaa2e4f65 100644 --- a/Analyser/Static/Enterprise/StaticAnalyser.cpp +++ b/Analyser/Static/Enterprise/StaticAnalyser.cpp @@ -98,6 +98,8 @@ Analyser::Static::TargetList Analyser::Static::Enterprise::GetTargets( if(type != 0x0500 || size != file->stats().st_size - 16) { target->media.file_bundles.clear(); + } else { + target->loading_command = "run \"file:\"\n"; } } diff --git a/Machines/Enterprise/EXOSCodes.hpp b/Machines/Enterprise/EXOSCodes.hpp index e66b59951..9af780a0f 100644 --- a/Machines/Enterprise/EXOSCodes.hpp +++ b/Machines/Enterprise/EXOSCodes.hpp @@ -54,101 +54,106 @@ enum class DeviceDescriptorFunction: uint8_t { BufferMoved = 13, }; -// Pages 68–70. enum class Error: uint8_t { NoError = 0x00, // - // General errors returned by the EXOS kernel. + // General Kernel Errors. // InvalidFunctionCode = 0xff, // IFUNC FunctionCallNotAllowed = 0xfe, // ILLFN - InvalidFunctionName = 0xfd, // INAME + InvalidString = 0xfd, // INAME InsufficientStack = 0xfc, // STACK ChannelIllegalOrDoesNotExist = 0xfb, // ICHAN DeviceDoesNotExist = 0xfa, // NODEV ChannelAlreadyExists = 0xf9, // CHANX NoAllocateBufferCallMade = 0xf8, // NOBUF - BadAllocateBufferParameters = 0xf7, // BADAL - InsufficientRAMForBuffer = 0xf6, // NORAM - InsufficientVideoRAM = 0xf5, // NOVID - NoFreeSegments = 0xf4, // NOSEG - InvalidSegment = 0xf3, // ISEG - InvalidUserBoundary = 0xf2, // IBOUN - InvalidEXOSVariableNumber = 0xf1, // IVAR - InvalidDesviceDescriptorType = 0xf0, // IDESC + InsufficientRAMForBuffer = 0xf7, // NORAM + InsufficientVideoRAM = 0xf6, // NOVID + NoFreeSegments = 0xf5, // NOSEG + InvalidSegment = 0xf4, // ISEG + InvalidUserBoundary = 0xf3, // IBOUND + InvalidEXOSVariableNumber = 0xf2, // IVAR + InvalidDesviceDescriptorType = 0xf1, // IDESC + UnrecognisedCommandString = 0xf0, // NOSTR + InvalidFileHeader = 0xef, // ASCII + UnknownModuleType = 0xee, // ITYPE + InvalidRelocatableModule = 0xed, // IREL + NoModule = 0xec, // NOMOD + InvalidTimeOrDateValue, // ITIME // - // General errors returned by various devices. + // General Device Errors. // - InvalidSpecialFunctionCode = 0xef, // ISPEC - AttemptToOpenSecondChannel = 0xee, // 2NDCH - FunctionNotSupported = 0xed, // NOFN - InvalidEscapeCharacter = 0xec, // ESC - StopKeyPressed = 0xeb, // STOP + InvalidSpecialFunctionCode = 0xea, // ISPEC + AttemptToOpenSecondChannel = 0xe9, // 2NDCH + InvalidUnitNumber = 0xe8, // IUNIT + FunctionNotSupported = 0xe7, // NOFN + InvalidEscapeSequence = 0xe6, // ESC + StopKeyPressed = 0xe5, // STOP + EndOfFileMetInRead = 0xe4, // EOF + ProtectionViolation = 0xe3, // PROT // - // File related errors. + // Device-Specific Errors. // - FileDoesNotExist = 0xea, // NOFIL - FileAlreadyExists = 0xe9, // EXFIL - FileAlreadyOpen = 0xe8, // FOPEN - EndOfFileMetInRead = 0xe7, // EOF - FileIsTooBig = 0xe6, // FSIZE - InvalidFilePointerValue = 0xe5, // FPTR - ProtectionViolation = 0xe4, // PROT - - // - // Keyboard errors. - // - InvalidFunctionKeyNumber = 0xe3, // KFKEY - RunOutOfFunctionKeySpace = 0xe2, // KFSPC - - // - // Sound errors. - // - EnvelopeInvalidOrTooBig = 0xe1, // SENV - NotEnoughRoomToDefineEnvelope = 0xe0, // SENDBF - EnvelopeStorageRequestedTooSmall = 0xdf, // SENFLO - SoundQueueFull = 0xde, // SQFUL - - // - // Video errors. - // - InvalidRowNumberToScroll = 0xdd, // VROW - AttemptToMoveCursorOffPage = 0xdc, // VCURS - InvalidColourPassedToINKOrPAPER = 0xdb, // VCOLR - InvalidXOrYSizeToOPEN = 0xda, // VSIZE - InvalidVideoModeToOPEN = 0xd9, // VMODE - BadParameterToDISPLAY = 0xdb, // VDISP, and officially 'naff' rather than 'bad' - NotEnoughRowsInPageToDISPLAY = 0xd7, // VDSP2 - AttemptToMoveBeamOffPage = 0xd6, // VBEAM - LineStyleTooBig = 0xd5, // VLSTY - LineModeTooBig = 0xd4, // VLMOD - CantDisplayCharacterOrGraphic = 0xd3, // VCHAR - - // - // Serial errors. - // - InvalidBaudRate = 0xd2, // BAUD - - // - // Editor errors. - // - InvalidVideoPageForOPEN = 0xd1, // EVID - TroubleInCommunicatingWithKeyboard = 0xd0, // EKEY - InvalidCoordinatesForPosition = 0xcf, // ECURS - - // - // Cassette errors. - // - CRCErrorFromCassetteDriver = 0xce, // CCRC - - // - // Network errors - // - SerialDeviceOpenCannotUseNetwork = 0xcd, // SEROP - ADDR_NETNotSetUp = 0xcc, // NOADR +// FileDoesNotExist = 0xea, // NOFIL +// FileAlreadyExists = 0xe9, // EXFIL +// FileAlreadyOpen = 0xe8, // FOPEN +// FileIsTooBig = 0xe6, // FSIZE +// InvalidFilePointerValue = 0xe5, // FPTR +// +// // +// // Keyboard errors. +// // +// InvalidFunctionKeyNumber = 0xe3, // KFKEY +// RunOutOfFunctionKeySpace = 0xe2, // KFSPC +// +// // +// // Sound errors. +// // +// EnvelopeInvalidOrTooBig = 0xe1, // SENV +// NotEnoughRoomToDefineEnvelope = 0xe0, // SENDBF +// EnvelopeStorageRequestedTooSmall = 0xdf, // SENFLO +// SoundQueueFull = 0xde, // SQFUL +// +// // +// // Video errors. +// // +// InvalidRowNumberToScroll = 0xdd, // VROW +// AttemptToMoveCursorOffPage = 0xdc, // VCURS +// InvalidColourPassedToINKOrPAPER = 0xdb, // VCOLR +// InvalidXOrYSizeToOPEN = 0xda, // VSIZE +// InvalidVideoModeToOPEN = 0xd9, // VMODE +// BadParameterToDISPLAY = 0xdb, // VDISP, and officially 'naff' rather than 'bad' +// NotEnoughRowsInPageToDISPLAY = 0xd7, // VDSP2 +// AttemptToMoveBeamOffPage = 0xd6, // VBEAM +// LineStyleTooBig = 0xd5, // VLSTY +// LineModeTooBig = 0xd4, // VLMOD +// CantDisplayCharacterOrGraphic = 0xd3, // VCHAR +// +// // +// // Serial errors. +// // +// InvalidBaudRate = 0xd2, // BAUD +// +// // +// // Editor errors. +// // +// InvalidVideoPageForOPEN = 0xd1, // EVID +// TroubleInCommunicatingWithKeyboard = 0xd0, // EKEY +// InvalidCoordinatesForPosition = 0xcf, // ECURS +// +// // +// // Cassette errors. +// // +// CRCErrorFromCassetteDriver = 0xce, // CCRC +// +// // +// // Network errors +// // +// SerialDeviceOpenCannotUseNetwork = 0xcd, // SEROP +// ADDR_NETNotSetUp = 0xcc, // NOADR }; } diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp index cb9d310a3..635b8cc3b 100644 --- a/Machines/Enterprise/HostFSHandler.cpp +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -72,14 +72,16 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui channels_.emplace(a, bundle_->open(name, Storage::FileMode::Read)); set_error(EXOS::Error::NoError); } catch(Storage::FileHolder::Error) { - set_error(EXOS::Error::FileDoesNotExist); +// set_error(EXOS::Error::FileDoesNotExist); + set_error(EXOS::Error::InvalidEscapeSequence); } } else { - set_error(EXOS::Error::FileAlreadyExists); +// set_error(EXOS::Error::FileAlreadyExists); + set_error(EXOS::Error::InvalidEscapeSequence); } } - // TODO: FileAlreadyOpen, FileAlreadyExists. + // TODO: what are appropriate error codes?. } break; // Page 54. diff --git a/Machines/Enterprise/HostFSHandler.hpp b/Machines/Enterprise/HostFSHandler.hpp index 8ebf6e7d9..421aa70fc 100644 --- a/Machines/Enterprise/HostFSHandler.hpp +++ b/Machines/Enterprise/HostFSHandler.hpp @@ -32,6 +32,7 @@ struct HostFSHandler { /// Sets the bundle of files on which this handler should operate. void set_file_bundle(std::shared_ptr bundle); + /// @returns A suitable in-client filing system ROM. std::vector rom(); private: From 822aa4155d195f04b62480613e506fe0b0ac242d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 21 Nov 2025 13:02:13 -0500 Subject: [PATCH 27/48] The read character is supposed to go into B. --- Machines/Enterprise/HostFSHandler.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp index 635b8cc3b..0c2744bbd 100644 --- a/Machines/Enterprise/HostFSHandler.cpp +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -17,8 +17,8 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui const auto set_error = [&](const EXOS::Error error) { a = uint8_t(error); }; - const auto set_c = [&](const uint8_t c) { - bc = (bc & 0xff00) | c; + const auto set_b = [&](const uint8_t ch) { + bc = uint16_t((bc & 0xffff) | (ch << 8)); }; const auto read_de = [&]() { @@ -107,7 +107,7 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui const auto next = channel->second.get(); if(!channel->second.eof()) { set_error(EXOS::Error::NoError); - set_c(next); + set_b(next); } else { set_error(EXOS::Error::EndOfFileMetInRead); } From c464ffaeac91dafd7dba35275f7aa8b41532c960 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 21 Nov 2025 13:08:52 -0500 Subject: [PATCH 28/48] Attempt read block. --- Machines/Enterprise/HostFSHandler.cpp | 31 +++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp index 0c2744bbd..da97ebb16 100644 --- a/Machines/Enterprise/HostFSHandler.cpp +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -24,6 +24,9 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui const auto read_de = [&]() { return accessor_.hostfs_read(de++); }; + const auto write_de = [&](const uint8_t ch) { + return accessor_.hostfs_write(de++, ch); + }; const auto find_channel = [&]() { if(a == 255) { return channels_.end(); @@ -97,7 +100,7 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui } break; // Page 55. - case uint8_t(EXOS::Function::ReadCharacter): + case uint8_t(EXOS::Function::ReadCharacter): { auto channel = find_channel(); if(channel == channels_.end()) { set_error(EXOS::Error::ChannelIllegalOrDoesNotExist); @@ -111,7 +114,31 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui } else { set_error(EXOS::Error::EndOfFileMetInRead); } - break; + } break; + + // Page 55. + case uint8_t(EXOS::Function::ReadBlock): { + auto channel = find_channel(); + if(channel == channels_.end()) { + set_error(EXOS::Error::ChannelIllegalOrDoesNotExist); + break; + } + + set_error(EXOS::Error::NoError); + while(true) { + const auto next = channel->second.get(); + if(!channel->second.eof()) { + bc--; + write_de(next); + if(!bc) { + break; + } + } else { + set_error(EXOS::Error::EndOfFileMetInRead); + break; + } + } + } break; } } From f4a9a64c93492f467aa18c44d86b76c0edf1c38c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 21 Nov 2025 13:11:05 -0500 Subject: [PATCH 29/48] Slightly rearrange. --- Machines/Enterprise/HostFSHandler.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp index da97ebb16..b1f9ce8c6 100644 --- a/Machines/Enterprise/HostFSHandler.cpp +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -108,11 +108,11 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui } const auto next = channel->second.get(); - if(!channel->second.eof()) { - set_error(EXOS::Error::NoError); - set_b(next); - } else { + if(channel->second.eof()) { set_error(EXOS::Error::EndOfFileMetInRead); + } else { + set_b(next); + set_error(EXOS::Error::NoError); } } break; @@ -124,19 +124,19 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui break; } - set_error(EXOS::Error::NoError); while(true) { const auto next = channel->second.get(); - if(!channel->second.eof()) { - bc--; - write_de(next); - if(!bc) { - break; - } - } else { + if(channel->second.eof()) { set_error(EXOS::Error::EndOfFileMetInRead); break; } + + write_de(next); + bc--; + if(!bc) { + set_error(EXOS::Error::NoError); + break; + } } } break; } From f287c80e39708a3305a5ba2192e6913cae36e934 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 21 Nov 2025 13:14:52 -0500 Subject: [PATCH 30/48] Pass on EXOS error code if received. --- Machines/Enterprise/HostFS/hostfs.z80s | 4 +++- Machines/Enterprise/HostFSHandler.cpp | 16 ++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Machines/Enterprise/HostFS/hostfs.z80s b/Machines/Enterprise/HostFS/hostfs.z80s index e53ae5166..53cde0031 100644 --- a/Machines/Enterprise/HostFS/hostfs.z80s +++ b/Machines/Enterprise/HostFS/hostfs.z80s @@ -90,9 +90,11 @@ allocate_exos_buffer: and a ret z - ; Otherwise, close the file again. What fun! + ; Otherwise, close the file again, then restore whatever the error was. What fun! + ld c, a ld a, b hostfscall 3 + ld a, c ret ; diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp index b1f9ce8c6..1f4cc46d7 100644 --- a/Machines/Enterprise/HostFSHandler.cpp +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -151,13 +151,13 @@ std::vector HostFSHandler::rom() { return std::vector{ 0x45, 0x58, 0x4f, 0x53, 0x5f, 0x52, 0x4f, 0x4d, 0x1b, 0x40, 0xc9, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x1d, 0x40, 0x00, 0x00, 0x04, 0x46, 0x49, 0x4c, 0x45, 0x0c, 0x00, 0x39, 0xc0, 0x3a, - 0xc0, 0x54, 0xc0, 0x5c, 0xc0, 0x61, 0xc0, 0x66, 0xc0, 0x6b, 0xc0, 0x70, 0xc0, 0x75, 0xc0, 0x7a, - 0xc0, 0x7f, 0xc0, 0x84, 0xc0, 0x89, 0xc0, 0x8e, 0xc0, 0xc9, 0x47, 0xed, 0xfe, 0xfe, 0x01, 0xa7, - 0xc0, 0xc5, 0x78, 0x01, 0x00, 0x00, 0x11, 0x01, 0x00, 0xf7, 0x1b, 0xc1, 0xa7, 0xc8, 0x78, 0xed, - 0xfe, 0xfe, 0x03, 0xc9, 0x47, 0xed, 0xfe, 0xfe, 0x02, 0xc3, 0x3f, 0xc0, 0xed, 0xfe, 0xfe, 0x03, - 0xc9, 0xed, 0xfe, 0xfe, 0x04, 0xc9, 0xed, 0xfe, 0xfe, 0x05, 0xc9, 0xed, 0xfe, 0xfe, 0x06, 0xc9, - 0xed, 0xfe, 0xfe, 0x07, 0xc9, 0xed, 0xfe, 0xfe, 0x08, 0xc9, 0xed, 0xfe, 0xfe, 0x09, 0xc9, 0xed, - 0xfe, 0xfe, 0x0a, 0xc9, 0xed, 0xfe, 0xfe, 0x0b, 0xc9, 0xed, 0xfe, 0xfe, 0x0c, 0xc9, 0xed, 0xfe, - 0xfe, 0x0d, 0xc9 + 0xc0, 0x56, 0xc0, 0x5e, 0xc0, 0x63, 0xc0, 0x68, 0xc0, 0x6d, 0xc0, 0x72, 0xc0, 0x77, 0xc0, 0x7c, + 0xc0, 0x81, 0xc0, 0x86, 0xc0, 0x8b, 0xc0, 0x90, 0xc0, 0xc9, 0x47, 0xed, 0xfe, 0xfe, 0x01, 0xa7, + 0xc0, 0xc5, 0x78, 0x01, 0x00, 0x00, 0x11, 0x01, 0x00, 0xf7, 0x1b, 0xc1, 0xa7, 0xc8, 0x4f, 0x78, + 0xed, 0xfe, 0xfe, 0x03, 0x79, 0xc9, 0x47, 0xed, 0xfe, 0xfe, 0x02, 0xc3, 0x3f, 0xc0, 0xed, 0xfe, + 0xfe, 0x03, 0xc9, 0xed, 0xfe, 0xfe, 0x04, 0xc9, 0xed, 0xfe, 0xfe, 0x05, 0xc9, 0xed, 0xfe, 0xfe, + 0x06, 0xc9, 0xed, 0xfe, 0xfe, 0x07, 0xc9, 0xed, 0xfe, 0xfe, 0x08, 0xc9, 0xed, 0xfe, 0xfe, 0x09, + 0xc9, 0xed, 0xfe, 0xfe, 0x0a, 0xc9, 0xed, 0xfe, 0xfe, 0x0b, 0xc9, 0xed, 0xfe, 0xfe, 0x0c, 0xc9, + 0xed, 0xfe, 0xfe, 0x0d, 0xc9 }; } From 364996021ec497e728831ea5afa2001b2c7937c9 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 21 Nov 2025 13:18:42 -0500 Subject: [PATCH 31/48] Deal correctly with BC = 0. --- Machines/Enterprise/HostFSHandler.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp index 1f4cc46d7..0ad2c5e07 100644 --- a/Machines/Enterprise/HostFSHandler.cpp +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -125,6 +125,11 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui } while(true) { + if(!bc) { + set_error(EXOS::Error::NoError); + break; + } + const auto next = channel->second.get(); if(channel->second.eof()) { set_error(EXOS::Error::EndOfFileMetInRead); @@ -133,10 +138,6 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui write_de(next); bc--; - if(!bc) { - set_error(EXOS::Error::NoError); - break; - } } } break; } From c193315e17ad8b3deb5c0e5825df37aead57a078 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 21 Nov 2025 13:28:45 -0500 Subject: [PATCH 32/48] Further consolidate for style. --- Machines/Enterprise/HostFSHandler.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp index 0ad2c5e07..9dfebc817 100644 --- a/Machines/Enterprise/HostFSHandler.cpp +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -124,20 +124,17 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui break; } - while(true) { - if(!bc) { - set_error(EXOS::Error::NoError); - break; - } - - const auto next = channel->second.get(); + auto &file = channel->second; + set_error(EXOS::Error::NoError); + while(bc) { + const auto next = file.get(); if(channel->second.eof()) { set_error(EXOS::Error::EndOfFileMetInRead); break; } write_de(next); - bc--; + --bc; } } break; } From 0cd192197120309236e525493f8049218a68a134 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 21 Nov 2025 15:54:43 -0500 Subject: [PATCH 33/48] Resolve addressing confusion. --- Analyser/Static/Enterprise/StaticAnalyser.cpp | 2 +- Machines/Enterprise/Enterprise.cpp | 44 +++++++++++-------- Machines/Enterprise/HostFSHandler.cpp | 2 +- Machines/Enterprise/HostFSHandler.hpp | 5 ++- Machines/Utility/ROMCatalogue.cpp | 9 ---- Machines/Utility/ROMCatalogue.hpp | 2 - 6 files changed, 32 insertions(+), 32 deletions(-) diff --git a/Analyser/Static/Enterprise/StaticAnalyser.cpp b/Analyser/Static/Enterprise/StaticAnalyser.cpp index aaa2e4f65..a78a60e75 100644 --- a/Analyser/Static/Enterprise/StaticAnalyser.cpp +++ b/Analyser/Static/Enterprise/StaticAnalyser.cpp @@ -96,7 +96,7 @@ Analyser::Static::TargetList Analyser::Static::Enterprise::GetTargets( // To consider: all the COMs I've seen also now have 12 bytes of 0 padding. // Test that? - if(type != 0x0500 || size != file->stats().st_size - 16) { + if(type != 0x0500 || size > file->stats().st_size - 16) { target->media.file_bundles.clear(); } else { target->loading_command = "run \"file:\"\n"; diff --git a/Machines/Enterprise/Enterprise.cpp b/Machines/Enterprise/Enterprise.cpp index 2b0dd624d..f7216f9f8 100644 --- a/Machines/Enterprise/Enterprise.cpp +++ b/Machines/Enterprise/Enterprise.cpp @@ -179,12 +179,6 @@ public: default: break; } - // Ensure a briddge into the local filing system if requested. - const bool needs_host_bridge = !target.media.file_bundles.empty(); -// if(needs_host_bridge) { -// request = request && ROM::Request(ROM::Name::EnterpriseEPFILEIO); -// } - // Get and validate ROMs. auto roms = rom_fetcher(request); if(!request.validate(roms)) { @@ -239,11 +233,9 @@ public: memcpy(exdos_rom_.data(), exdos->second.data(), std::min(exdos_rom_.size(), exdos->second.size())); } - // Possibly grab the file IO ROM. -// const auto host_fs = roms.find(ROM::Name::EnterpriseEPFILEIO); -// host_fs_rom_.fill(0xff); - if(needs_host_bridge) {//host_fs != roms.end()) { -// memcpy(host_fs_rom_.data(), host_fs->second.data(), std::min(host_fs_rom_.size(), host_fs->second.size())); + // Possibly install the host FS ROM. + host_fs_rom_.fill(0xff); + if(!target.media.file_bundles.empty()) { const auto rom = host_fs_.rom(); std::copy(rom.begin(), rom.end(), host_fs_rom_.begin()); find_host_fs_hooks(); @@ -561,6 +553,13 @@ public: break; case PartialMachineCycle::ReadOpcode: + { + static bool print_opcode = false; + if(print_opcode) { + printf("%04x: %02x\n", address, read_pointers_[address >> 14][address]); + } + } + // Potential segue for the host FS. I'm relying on branch prediction to // avoid this cost almost always. if(test_host_fs_traps_ && (address >> 14) == 3) [[unlikely]] { @@ -625,6 +624,12 @@ private: std::array host_fs_rom_; const uint8_t min_ram_slot_; + uint8_t *ram_segment(const uint8_t page) { + if(page < min_ram_slot_) return nullptr; + const auto ram_floor = 4194304 - ram_.size(); + return &ram_[(page << 14) - ram_floor]; + } + const uint8_t *read_pointers_[4] = {nullptr, nullptr, nullptr, nullptr}; uint8_t *write_pointers_[4] = {nullptr, nullptr, nullptr, nullptr}; uint8_t pages_[4] = {0x80, 0x80, 0x80, 0x80}; @@ -663,10 +668,9 @@ private: // at least while the RAM is the first thing declared above, does a little // to benefit data locality. Albeit not in a useful sense. if(offset >= min_ram_slot_) { - const auto ram_floor = 4194304 - ram_.size(); - const size_t address = offset * 0x4000 - ram_floor; is_video_[slot] = offset >= 0xfc; // TODO: this hard-codes a 64kb video assumption. - page(&ram_[address], &ram_[address]); + auto pointer = ram_segment(offset); + page(pointer, pointer); return; } @@ -820,10 +824,14 @@ private: } } - void hostfs_write(const uint16_t address, const uint8_t value) override { - if(write_pointers_[address >> 14]) { - write_pointers_[address >> 14][address] = value; - } + void hostfs_user_write(const uint16_t address, const uint8_t value) override { + // "User" writes go to to wherever the user last had paged; + // per 5.4 System Segment Usage those pages are stored in memory from + // 0xbffc, so grab from there. + const auto page_id = address >> 14; + const auto page = read_pointers_[0xbffc >> 14] ? read_pointers_[0xbffc >> 14][0xbffc + page_id] : 0xff; + const auto offset = address & 0x3fff; + ram_segment(page)[offset] = value; } void find_host_fs_hooks() { diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp index 9dfebc817..d5976c7fc 100644 --- a/Machines/Enterprise/HostFSHandler.cpp +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -25,7 +25,7 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui return accessor_.hostfs_read(de++); }; const auto write_de = [&](const uint8_t ch) { - return accessor_.hostfs_write(de++, ch); + return accessor_.hostfs_user_write(de++, ch); }; const auto find_channel = [&]() { if(a == 255) { diff --git a/Machines/Enterprise/HostFSHandler.hpp b/Machines/Enterprise/HostFSHandler.hpp index 421aa70fc..40901844f 100644 --- a/Machines/Enterprise/HostFSHandler.hpp +++ b/Machines/Enterprise/HostFSHandler.hpp @@ -20,7 +20,10 @@ namespace Enterprise { struct HostFSHandler { struct MemoryAccessor { virtual uint8_t hostfs_read(uint16_t) = 0; - virtual void hostfs_write(uint16_t, uint8_t) = 0; +// virtual void hostfs_write(uint16_t, uint8_t) = 0; + +// virtual uint8_t hostfs_user_read(uint16_t) = 0; + virtual void hostfs_user_write(uint16_t, uint8_t) = 0; }; HostFSHandler(MemoryAccessor &); diff --git a/Machines/Utility/ROMCatalogue.cpp b/Machines/Utility/ROMCatalogue.cpp index 99a590998..d5b2355fb 100644 --- a/Machines/Utility/ROMCatalogue.cpp +++ b/Machines/Utility/ROMCatalogue.cpp @@ -670,15 +670,6 @@ const std::vector &Description::all_roms() { 0xe6daa0e9u }, - { - EnterpriseEPFILEIO, - "Enterprise", - "the EP128Emu direction FILE IO ROM", - "epfileio.rom", - 16_kb, - 0x60c79925u - }, - // // Macintosh // diff --git a/Machines/Utility/ROMCatalogue.hpp b/Machines/Utility/ROMCatalogue.hpp index 88b621db4..3d46e70cd 100644 --- a/Machines/Utility/ROMCatalogue.hpp +++ b/Machines/Utility/ROMCatalogue.hpp @@ -118,8 +118,6 @@ enum Name { EnterpriseEPDOS, EnterpriseEXDOS, - EnterpriseEPFILEIO, - // Macintosh. Macintosh128k, Macintosh512k, From 1b977cae644ef2344d235f03643b22ddc788beec Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 21 Nov 2025 15:56:44 -0500 Subject: [PATCH 34/48] Add TODO. --- Machines/Enterprise/HostFS/hostfs.z80s | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Machines/Enterprise/HostFS/hostfs.z80s b/Machines/Enterprise/HostFS/hostfs.z80s index 53cde0031..956e727ea 100644 --- a/Machines/Enterprise/HostFS/hostfs.z80s +++ b/Machines/Enterprise/HostFS/hostfs.z80s @@ -27,6 +27,10 @@ dw 0x4000 + (device_chain & 0x3fff) ; ROM entry point; there is no startup code. + ; + ; Possibly TODO: use EXOS call 19 to set this as the default filing system. + ; Disk dives do this, it's not unprecedented. + ; ret dw 0 ; XX_NEXT_LOW/HI: Pointer to start of next device. There is no next device. From ac9fc1598121d638176049092cae2088e3951056 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 21 Nov 2025 17:03:28 -0500 Subject: [PATCH 35/48] Banish magic constant. --- Machines/Enterprise/Enterprise.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Machines/Enterprise/Enterprise.cpp b/Machines/Enterprise/Enterprise.cpp index f7216f9f8..852aaa98a 100644 --- a/Machines/Enterprise/Enterprise.cpp +++ b/Machines/Enterprise/Enterprise.cpp @@ -626,7 +626,9 @@ private: uint8_t *ram_segment(const uint8_t page) { if(page < min_ram_slot_) return nullptr; - const auto ram_floor = 4194304 - ram_.size(); + const auto ram_floor = (0x100 << 14) - ram_.size(); + // Each segment is 2^14 bytes long and there are 256 of them. So the Enterprise has a 22-bit address space. + // RAM is at the end of that range; `ram_floor` is the 22-bit address at which RAM starts. return &ram_[(page << 14) - ram_floor]; } From c4bab00c6de2bccc19c378239e3779559946f057 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 21 Nov 2025 21:06:00 -0500 Subject: [PATCH 36/48] Set file: as default filing system when in use. --- Machines/Enterprise/HostFS/hostfs.z80s | 16 +++++++++++----- Machines/Enterprise/HostFSHandler.cpp | 23 +++++++++++++---------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/Machines/Enterprise/HostFS/hostfs.z80s b/Machines/Enterprise/HostFS/hostfs.z80s index 956e727ea..7e961c352 100644 --- a/Machines/Enterprise/HostFS/hostfs.z80s +++ b/Machines/Enterprise/HostFS/hostfs.z80s @@ -26,11 +26,7 @@ ; ROM is paged at $4000, though when executed from it'll be at $c000. dw 0x4000 + (device_chain & 0x3fff) - ; ROM entry point; there is no startup code. - ; - ; Possibly TODO: use EXOS call 19 to set this as the default filing system. - ; Disk dives do this, it's not unprecedented. - ; + ; ROM entry point; handle nothing. ret dw 0 ; XX_NEXT_LOW/HI: Pointer to start of next device. There is no next device. @@ -45,6 +41,7 @@ device_chain_type: db 0 ; DD_TAB_LOW/HI/SEG: db 0 ; DD_UNIT_COUNT: ? +device_name: db 4 dm "FILE" ; DD_NAME @@ -177,6 +174,15 @@ call11: ; call12: hostfscall 12 + + ; + ; Set this as the default filing system. + ; Disk dives do this, it's not unprecedented. + ; + ld de, device_name + ld c, 1 + rst 0x30 + db 19 ret ; diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp index d5976c7fc..d75acec62 100644 --- a/Machines/Enterprise/HostFSHandler.cpp +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -147,15 +147,18 @@ void HostFSHandler::set_file_bundle(std::shared_ptr HostFSHandler::rom() { // Assembled and transcribed from hostfs.z80. return std::vector{ - 0x45, 0x58, 0x4f, 0x53, 0x5f, 0x52, 0x4f, 0x4d, 0x1b, 0x40, 0xc9, 0x00, 0x00, 0xfe, 0xff, 0x00, - 0x00, 0x00, 0x1d, 0x40, 0x00, 0x00, 0x04, 0x46, 0x49, 0x4c, 0x45, 0x0c, 0x00, 0x39, 0xc0, 0x3a, - 0xc0, 0x56, 0xc0, 0x5e, 0xc0, 0x63, 0xc0, 0x68, 0xc0, 0x6d, 0xc0, 0x72, 0xc0, 0x77, 0xc0, 0x7c, - 0xc0, 0x81, 0xc0, 0x86, 0xc0, 0x8b, 0xc0, 0x90, 0xc0, 0xc9, 0x47, 0xed, 0xfe, 0xfe, 0x01, 0xa7, - 0xc0, 0xc5, 0x78, 0x01, 0x00, 0x00, 0x11, 0x01, 0x00, 0xf7, 0x1b, 0xc1, 0xa7, 0xc8, 0x4f, 0x78, - 0xed, 0xfe, 0xfe, 0x03, 0x79, 0xc9, 0x47, 0xed, 0xfe, 0xfe, 0x02, 0xc3, 0x3f, 0xc0, 0xed, 0xfe, - 0xfe, 0x03, 0xc9, 0xed, 0xfe, 0xfe, 0x04, 0xc9, 0xed, 0xfe, 0xfe, 0x05, 0xc9, 0xed, 0xfe, 0xfe, - 0x06, 0xc9, 0xed, 0xfe, 0xfe, 0x07, 0xc9, 0xed, 0xfe, 0xfe, 0x08, 0xc9, 0xed, 0xfe, 0xfe, 0x09, - 0xc9, 0xed, 0xfe, 0xfe, 0x0a, 0xc9, 0xed, 0xfe, 0xfe, 0x0b, 0xc9, 0xed, 0xfe, 0xfe, 0x0c, 0xc9, - 0xed, 0xfe, 0xfe, 0x0d, 0xc9 + 0x45, 0x58, 0x4f, 0x53, 0x5f, 0x52, 0x4f, 0x4d, 0x1b, 0x40, 0xc9, 0x00, + 0x00, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x1d, 0x40, 0x00, 0x00, 0x04, 0x46, + 0x49, 0x4c, 0x45, 0x0c, 0x00, 0x39, 0xc0, 0x3a, 0xc0, 0x56, 0xc0, 0x5e, + 0xc0, 0x63, 0xc0, 0x68, 0xc0, 0x6d, 0xc0, 0x72, 0xc0, 0x77, 0xc0, 0x7c, + 0xc0, 0x81, 0xc0, 0x86, 0xc0, 0x8b, 0xc0, 0x97, 0xc0, 0xc9, 0x47, 0xed, + 0xfe, 0xfe, 0x01, 0xa7, 0xc0, 0xc5, 0x78, 0x01, 0x00, 0x00, 0x11, 0x01, + 0x00, 0xf7, 0x1b, 0xc1, 0xa7, 0xc8, 0x4f, 0x78, 0xed, 0xfe, 0xfe, 0x03, + 0x79, 0xc9, 0x47, 0xed, 0xfe, 0xfe, 0x02, 0xc3, 0x3f, 0xc0, 0xed, 0xfe, + 0xfe, 0x03, 0xc9, 0xed, 0xfe, 0xfe, 0x04, 0xc9, 0xed, 0xfe, 0xfe, 0x05, + 0xc9, 0xed, 0xfe, 0xfe, 0x06, 0xc9, 0xed, 0xfe, 0xfe, 0x07, 0xc9, 0xed, + 0xfe, 0xfe, 0x08, 0xc9, 0xed, 0xfe, 0xfe, 0x09, 0xc9, 0xed, 0xfe, 0xfe, + 0x0a, 0xc9, 0xed, 0xfe, 0xfe, 0x0b, 0xc9, 0xed, 0xfe, 0xfe, 0x0c, 0x11, + 0x16, 0xc0, 0x0e, 0x01, 0xf7, 0x13, 0xc9, 0xed, 0xfe, 0xfe, 0x0d, 0xc9 }; } From 76ed9d1703518faf5250dd898d89727579e2360d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 21 Nov 2025 21:11:27 -0500 Subject: [PATCH 37/48] Add FileMode to key file loader. --- Analyser/Static/Enterprise/StaticAnalyser.cpp | 2 +- Machines/Enterprise/Enterprise.cpp | 4 ++-- Machines/Enterprise/HostFS/compile.sh | 2 +- Storage/FileBundle/FileBundle.cpp | 4 ++-- Storage/FileBundle/FileBundle.hpp | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Analyser/Static/Enterprise/StaticAnalyser.cpp b/Analyser/Static/Enterprise/StaticAnalyser.cpp index a78a60e75..cf9603d15 100644 --- a/Analyser/Static/Enterprise/StaticAnalyser.cpp +++ b/Analyser/Static/Enterprise/StaticAnalyser.cpp @@ -87,7 +87,7 @@ Analyser::Static::TargetList Analyser::Static::Enterprise::GetTargets( } if(!media.file_bundles.empty()) { - auto file = media.file_bundles.front()->key_file(); + auto file = media.file_bundles.front()->key_file(Storage::FileMode::Read); if(file.has_value()) { // Check for a .COM by inspecting the header. diff --git a/Machines/Enterprise/Enterprise.cpp b/Machines/Enterprise/Enterprise.cpp index 852aaa98a..69a8ffd96 100644 --- a/Machines/Enterprise/Enterprise.cpp +++ b/Machines/Enterprise/Enterprise.cpp @@ -106,10 +106,10 @@ public: ConcreteMachine(const Analyser::Static::Enterprise::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : min_ram_slot_(min_ram_slot(target)), z80_(*this), - host_fs_(*this), nick_(ram_.end() - 65536), dave_audio_(audio_queue_), - speaker_(dave_audio_) { + speaker_(dave_audio_), + host_fs_(*this) { // Request a clock of 4Mhz; this'll be mapped upwards for Nick and downwards for Dave elsewhere. set_clock_rate(clock_rate); diff --git a/Machines/Enterprise/HostFS/compile.sh b/Machines/Enterprise/HostFS/compile.sh index 9339a9564..1e832908f 100644 --- a/Machines/Enterprise/HostFS/compile.sh +++ b/Machines/Enterprise/HostFS/compile.sh @@ -1 +1 @@ -pyz80 --obj=hostfs.rom hostfs.z80s +pyz80 --obj=hostfs.rom hostfs.z80s && xxd -i hostfs.rom diff --git a/Storage/FileBundle/FileBundle.cpp b/Storage/FileBundle/FileBundle.cpp index 3791bbfeb..380600faf 100644 --- a/Storage/FileBundle/FileBundle.cpp +++ b/Storage/FileBundle/FileBundle.cpp @@ -10,8 +10,8 @@ using namespace Storage::FileBundle; -std::optional LocalFSFileBundle::key_file() { - return {to_contain_}; +std::optional LocalFSFileBundle::key_file(const Storage::FileMode mode) { + return Storage::FileHolder(to_contain_, mode); } Storage::FileHolder LocalFSFileBundle::open(const std::string &name, Storage::FileMode mode) { diff --git a/Storage/FileBundle/FileBundle.hpp b/Storage/FileBundle/FileBundle.hpp index 680c01637..7b0028fbc 100644 --- a/Storage/FileBundle/FileBundle.hpp +++ b/Storage/FileBundle/FileBundle.hpp @@ -23,7 +23,7 @@ namespace Storage::FileBundle { bundles in the future. */ struct FileBundle { - virtual std::optional key_file() = 0; + virtual std::optional key_file(FileMode) = 0; virtual FileHolder open(const std::string &, FileMode) = 0; }; @@ -31,7 +31,7 @@ struct FileBundle { struct LocalFSFileBundle: public FileBundle { LocalFSFileBundle(const std::string &to_contain) : to_contain_(to_contain) {} - std::optional key_file() override; + std::optional key_file(FileMode) override; FileHolder open(const std::string &, FileMode) override; private: From 424d57c2c19a110e66997f5e7727d7a3836a4a88 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 21 Nov 2025 21:30:02 -0500 Subject: [PATCH 38/48] Fix executable name. --- Machines/Enterprise/HostFS/compile.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 Machines/Enterprise/HostFS/compile.sh diff --git a/Machines/Enterprise/HostFS/compile.sh b/Machines/Enterprise/HostFS/compile.sh old mode 100644 new mode 100755 index 1e832908f..d399515a6 --- a/Machines/Enterprise/HostFS/compile.sh +++ b/Machines/Enterprise/HostFS/compile.sh @@ -1 +1 @@ -pyz80 --obj=hostfs.rom hostfs.z80s && xxd -i hostfs.rom +pyz80.py --obj=hostfs.rom hostfs.z80s && xxd -i hostfs.rom From 50adbaefc88798080ad51d65d9cdb3248c00b402 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 21 Nov 2025 21:53:53 -0500 Subject: [PATCH 39/48] Support BAS files, use file name from guest. --- Analyser/Static/Enterprise/StaticAnalyser.cpp | 21 ++++++++++++------- Analyser/Static/StaticAnalyser.cpp | 3 ++- Machines/Enterprise/HostFSHandler.cpp | 7 +++++++ OSBindings/Mac/Clock Signal/Info.plist | 1 + Storage/FileBundle/FileBundle.cpp | 19 +++++++++++------ Storage/FileBundle/FileBundle.hpp | 9 ++++---- 6 files changed, 42 insertions(+), 18 deletions(-) diff --git a/Analyser/Static/Enterprise/StaticAnalyser.cpp b/Analyser/Static/Enterprise/StaticAnalyser.cpp index cf9603d15..daee4ca47 100644 --- a/Analyser/Static/Enterprise/StaticAnalyser.cpp +++ b/Analyser/Static/Enterprise/StaticAnalyser.cpp @@ -87,16 +87,23 @@ Analyser::Static::TargetList Analyser::Static::Enterprise::GetTargets( } if(!media.file_bundles.empty()) { - auto file = media.file_bundles.front()->key_file(Storage::FileMode::Read); + auto &bundle = *media.file_bundles.front(); + const auto key = bundle.key_file(); + + if(key.has_value()) { + auto file = bundle.open(*key, Storage::FileMode::Read); + + enum class FileType: uint16_t { + COM = 0x0500, + BAS = 0x0400, + }; - if(file.has_value()) { // Check for a .COM by inspecting the header. - const uint16_t type = file->get_le(); - const uint16_t size = file->get_le(); - // To consider: all the COMs I've seen also now have 12 bytes of 0 padding. - // Test that? + const auto type = FileType(file.get_le()); + const uint16_t size = file.get_le(); + // There are then 12 bytes of 0 padding that could be tested for. - if(type != 0x0500 || size > file->stats().st_size - 16) { + if((type != FileType::COM && type != FileType::BAS) || size > file.stats().st_size - 16) { target->media.file_bundles.clear(); } else { target->loading_command = "run \"file:\"\n"; diff --git a/Analyser/Static/StaticAnalyser.cpp b/Analyser/Static/StaticAnalyser.cpp index 3e9a5b409..e325aa19f 100644 --- a/Analyser/Static/StaticAnalyser.cpp +++ b/Analyser/Static/StaticAnalyser.cpp @@ -213,8 +213,8 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform:: accumulator.try_standard>(TargetPlatform::Acorn, "adf"); accumulator.try_standard>(TargetPlatform::Amiga, "adf"); accumulator.try_standard>(TargetPlatform::Acorn, "adl"); - accumulator.try_standard>(TargetPlatform::Archimedes, "jfd"); + accumulator.try_standard(TargetPlatform::Enterprise, "bas"); accumulator.try_standard(TargetPlatform::AllCartridge, "bin"); accumulator.try_standard(TargetPlatform::MSX, "cas"); @@ -263,6 +263,7 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform:: TargetPlatform::Amiga | TargetPlatform::AtariST | TargetPlatform::AmstradCPC | TargetPlatform::ZXSpectrum, "ipf"); + accumulator.try_standard>(TargetPlatform::Archimedes, "jfd"); accumulator.try_standard>(TargetPlatform::AtariST, "msa"); accumulator.try_standard(TargetPlatform::MSX, "mx2"); accumulator.try_standard>(TargetPlatform::DiskII, "nib"); diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp index d75acec62..3a528c3de 100644 --- a/Machines/Enterprise/HostFSHandler.cpp +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -60,6 +60,13 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui name.push_back(char(read_de())); } + // Use the key file if no name is specified. + if(name.empty()) { + if(const auto key_file = bundle_->key_file(); key_file.has_value()) { + name = *key_file; + } + } + // The only difference between open and create is that the former is // meant to append. const auto mode = diff --git a/OSBindings/Mac/Clock Signal/Info.plist b/OSBindings/Mac/Clock Signal/Info.plist index a4c44e12e..cdd67210c 100644 --- a/OSBindings/Mac/Clock Signal/Info.plist +++ b/OSBindings/Mac/Clock Signal/Info.plist @@ -811,6 +811,7 @@ CFBundleTypeExtensions com + bas CFBundleTypeIconFile diff --git a/Storage/FileBundle/FileBundle.cpp b/Storage/FileBundle/FileBundle.cpp index 380600faf..afdb5eaaf 100644 --- a/Storage/FileBundle/FileBundle.cpp +++ b/Storage/FileBundle/FileBundle.cpp @@ -10,13 +10,20 @@ using namespace Storage::FileBundle; -std::optional LocalFSFileBundle::key_file(const Storage::FileMode mode) { - return Storage::FileHolder(to_contain_, mode); +LocalFSFileBundle::LocalFSFileBundle(const std::string &to_contain) { + const auto last_separator = to_contain.find_last_of("/\\"); + if(last_separator == std::string::npos) { + key_file_ = to_contain; + } else { + base_path_ = to_contain.substr(0, last_separator + 1); + key_file_ = to_contain.substr(last_separator + 1); + } +} + +std::optional LocalFSFileBundle::key_file() { + return key_file_; } Storage::FileHolder LocalFSFileBundle::open(const std::string &name, Storage::FileMode mode) { - // TODO: append path. Just cheat for now. - (void)name; - return {/* name */ to_contain_, mode}; + return Storage::FileHolder(base_path_ + name, mode); } - diff --git a/Storage/FileBundle/FileBundle.hpp b/Storage/FileBundle/FileBundle.hpp index 7b0028fbc..87fe04c71 100644 --- a/Storage/FileBundle/FileBundle.hpp +++ b/Storage/FileBundle/FileBundle.hpp @@ -23,19 +23,20 @@ namespace Storage::FileBundle { bundles in the future. */ struct FileBundle { - virtual std::optional key_file(FileMode) = 0; + virtual std::optional key_file() = 0; virtual FileHolder open(const std::string &, FileMode) = 0; }; struct LocalFSFileBundle: public FileBundle { - LocalFSFileBundle(const std::string &to_contain) : to_contain_(to_contain) {} + LocalFSFileBundle(const std::string &to_contain); - std::optional key_file(FileMode) override; + std::optional key_file() override; FileHolder open(const std::string &, FileMode) override; private: - std::string to_contain_; + std::string key_file_; + std::string base_path_; }; }; From 5ef54788614e1c0e39f2886dc068dc69f0cf8b44 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 21 Nov 2025 21:54:43 -0500 Subject: [PATCH 40/48] Resolve signedness warning. --- Machines/Enterprise/Enterprise.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Machines/Enterprise/Enterprise.cpp b/Machines/Enterprise/Enterprise.cpp index 69a8ffd96..7fe6fe25b 100644 --- a/Machines/Enterprise/Enterprise.cpp +++ b/Machines/Enterprise/Enterprise.cpp @@ -629,7 +629,7 @@ private: const auto ram_floor = (0x100 << 14) - ram_.size(); // Each segment is 2^14 bytes long and there are 256 of them. So the Enterprise has a 22-bit address space. // RAM is at the end of that range; `ram_floor` is the 22-bit address at which RAM starts. - return &ram_[(page << 14) - ram_floor]; + return &ram_[size_t((page << 14) - ram_floor)]; } const uint8_t *read_pointers_[4] = {nullptr, nullptr, nullptr, nullptr}; From 411b96128cb48fb828dfee694965ecb628ae957a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 21 Nov 2025 22:07:30 -0500 Subject: [PATCH 41/48] Further address type conversions. --- Machines/Enterprise/Enterprise.cpp | 6 +++--- Storage/FileBundle/FileBundle.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Machines/Enterprise/Enterprise.cpp b/Machines/Enterprise/Enterprise.cpp index 7fe6fe25b..143d0b50f 100644 --- a/Machines/Enterprise/Enterprise.cpp +++ b/Machines/Enterprise/Enterprise.cpp @@ -629,7 +629,7 @@ private: const auto ram_floor = (0x100 << 14) - ram_.size(); // Each segment is 2^14 bytes long and there are 256 of them. So the Enterprise has a 22-bit address space. // RAM is at the end of that range; `ram_floor` is the 22-bit address at which RAM starts. - return &ram_[size_t((page << 14) - ram_floor)]; + return &ram_[size_t((page << 14)) - ram_floor]; } const uint8_t *read_pointers_[4] = {nullptr, nullptr, nullptr, nullptr}; @@ -831,7 +831,7 @@ private: // per 5.4 System Segment Usage those pages are stored in memory from // 0xbffc, so grab from there. const auto page_id = address >> 14; - const auto page = read_pointers_[0xbffc >> 14] ? read_pointers_[0xbffc >> 14][0xbffc + page_id] : 0xff; + const uint8_t page = read_pointers_[0xbffc >> 14] ? read_pointers_[0xbffc >> 14][0xbffc + page_id] : 0xff; const auto offset = address & 0x3fff; ram_segment(page)[offset] = value; } @@ -853,7 +853,7 @@ private: } const auto offset = begin - host_fs_rom_.begin() + 0xc000; // ROM will be paged in slot 3, i.e. at $c000. - host_fs_traps_.insert(offset); + host_fs_traps_.insert(uint16_t(offset)); // Move function code up to where this trap was, and NOP out the tail. begin[0] = begin[3]; diff --git a/Storage/FileBundle/FileBundle.cpp b/Storage/FileBundle/FileBundle.cpp index afdb5eaaf..9ce9cae79 100644 --- a/Storage/FileBundle/FileBundle.cpp +++ b/Storage/FileBundle/FileBundle.cpp @@ -24,6 +24,6 @@ std::optional LocalFSFileBundle::key_file() { return key_file_; } -Storage::FileHolder LocalFSFileBundle::open(const std::string &name, Storage::FileMode mode) { +Storage::FileHolder LocalFSFileBundle::open(const std::string &name, const Storage::FileMode mode) { return Storage::FileHolder(base_path_ + name, mode); } From 7b513a95a1d628dbd7c0bfdc9b8e523ff923172c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 21 Nov 2025 22:24:31 -0500 Subject: [PATCH 42/48] Restructure, separating create from open. --- Machines/Enterprise/HostFSHandler.cpp | 166 +++++++++++++------------- 1 file changed, 85 insertions(+), 81 deletions(-) diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp index 3a528c3de..3afb9dac5 100644 --- a/Machines/Enterprise/HostFSHandler.cpp +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -17,103 +17,113 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui const auto set_error = [&](const EXOS::Error error) { a = uint8_t(error); }; - const auto set_b = [&](const uint8_t ch) { - bc = uint16_t((bc & 0xffff) | (ch << 8)); - }; - const auto read_de = [&]() { return accessor_.hostfs_read(de++); }; + const auto read_name = [&]() { + // Get name. + uint8_t length = read_de(); + std::string name; + while(length--) { + name.push_back(char(read_de())); + } + + // Use the key file if no name is specified. + if(name.empty()) { + if(const auto key_file = bundle_->key_file(); key_file.has_value()) { + name = *key_file; + } + } + + return name; + }; + const auto set_b = [&](const uint8_t ch) { + bc = uint16_t((bc & 0xffff) | (ch << 8)); + }; const auto write_de = [&](const uint8_t ch) { return accessor_.hostfs_user_write(de++, ch); }; - const auto find_channel = [&]() { + + // + // Functions that don't require an existing channel. + // + switch(function) { + default: break; + + case uint8_t(EXOS::DeviceDescriptorFunction::Initialise): + channels_.clear(); + set_error(EXOS::Error::NoError); + return; + + // Page 54. + // Emprically: C contains the unit number. + case uint8_t(EXOS::Function::OpenChannel): { + if(a == 255) { + set_error(EXOS::Error::ChannelIllegalOrDoesNotExist); + break; + } + const auto name = read_name(); + + try { + channels_.emplace(a, bundle_->open(name, Storage::FileMode::ReadWrite)); + set_error(EXOS::Error::NoError); + } catch(Storage::FileHolder::Error) { + try { + channels_.emplace(a, bundle_->open(name, Storage::FileMode::Read)); + set_error(EXOS::Error::NoError); + } catch(Storage::FileHolder::Error) { +// set_error(EXOS::Error::FileDoesNotExist); + set_error(EXOS::Error::ProtectionViolation); + } + } + } + return; + + // Page 54. + case uint8_t(EXOS::Function::CreateChannel): { + if(a == 255) { + set_error(EXOS::Error::ChannelIllegalOrDoesNotExist); + break; + } + const auto name = read_name(); + + try { + channels_.emplace(a, bundle_->open(name, Storage::FileMode::Rewrite)); + set_error(EXOS::Error::NoError); + } catch(Storage::FileHolder::Error) { +// set_error(EXOS::Error::FileAlreadyExists); + set_error(EXOS::Error::ProtectionViolation); + } + } return; + } + + // + // Functions from here require a channel already open. + // + const auto channel = [&]() { if(a == 255) { return channels_.end(); } return channels_.find(a); - }; + } (); + if(channel == channels_.end()) { + set_error(EXOS::Error::ChannelIllegalOrDoesNotExist); + return; + } switch(function) { default: printf("UNIMPLEMENTED function %d with A:%02x BC:%04x DE:%04x\n", function, a, bc, de); break; - case uint8_t(EXOS::DeviceDescriptorFunction::Initialise): - channels_.clear(); - set_error(EXOS::Error::NoError); - break; - // Page 54. - // Emprically: C contains the unit number. - case uint8_t(EXOS::Function::OpenChannel): - case uint8_t(EXOS::Function::CreateChannel): { - if(a == 255) { - set_error(EXOS::Error::ChannelIllegalOrDoesNotExist); - break; - } - - // Get name. - uint8_t length = read_de(); - std::string name; - while(length--) { - name.push_back(char(read_de())); - } - - // Use the key file if no name is specified. - if(name.empty()) { - if(const auto key_file = bundle_->key_file(); key_file.has_value()) { - name = *key_file; - } - } - - // The only difference between open and create is that the former is - // meant to append. - const auto mode = - function == uint8_t(EXOS::Function::CreateChannel) ? - Storage::FileMode::Rewrite : Storage::FileMode::ReadWrite; - - try { - channels_.emplace(a, bundle_->open(name, mode)); - set_error(EXOS::Error::NoError); - } catch(Storage::FileHolder::Error) { - if(mode == Storage::FileMode::ReadWrite) { - try { - channels_.emplace(a, bundle_->open(name, Storage::FileMode::Read)); - set_error(EXOS::Error::NoError); - } catch(Storage::FileHolder::Error) { -// set_error(EXOS::Error::FileDoesNotExist); - set_error(EXOS::Error::InvalidEscapeSequence); - } - } else { -// set_error(EXOS::Error::FileAlreadyExists); - set_error(EXOS::Error::InvalidEscapeSequence); - } - } - - // TODO: what are appropriate error codes?. - } break; - - // Page 54. - case uint8_t(EXOS::Function::CloseChannel): { - auto channel = find_channel(); - if(channel == channels_.end()) { - set_error(EXOS::Error::ChannelIllegalOrDoesNotExist); - break; - } - + case uint8_t(EXOS::Function::CloseChannel): set_error(EXOS::Error::NoError); channels_.erase(channel); - } break; + break; // Page 55. case uint8_t(EXOS::Function::ReadCharacter): { - auto channel = find_channel(); - if(channel == channels_.end()) { - set_error(EXOS::Error::ChannelIllegalOrDoesNotExist); - break; - } - const auto next = channel->second.get(); if(channel->second.eof()) { set_error(EXOS::Error::EndOfFileMetInRead); @@ -125,12 +135,6 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui // Page 55. case uint8_t(EXOS::Function::ReadBlock): { - auto channel = find_channel(); - if(channel == channels_.end()) { - set_error(EXOS::Error::ChannelIllegalOrDoesNotExist); - break; - } - auto &file = channel->second; set_error(EXOS::Error::NoError); while(bc) { From 10847b3e0b00fc76fd46f8b2c60fa0d9eb36772e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 21 Nov 2025 22:44:49 -0500 Subject: [PATCH 43/48] Fill in everything except `SetChannelStatus`. --- Machines/Enterprise/Enterprise.cpp | 14 +++++-- Machines/Enterprise/HostFSHandler.cpp | 53 +++++++++++++++++++++++++-- Machines/Enterprise/HostFSHandler.hpp | 5 ++- Storage/FileHolder.cpp | 4 +- Storage/FileHolder.hpp | 7 +++- 5 files changed, 72 insertions(+), 11 deletions(-) diff --git a/Machines/Enterprise/Enterprise.cpp b/Machines/Enterprise/Enterprise.cpp index 143d0b50f..be126eb80 100644 --- a/Machines/Enterprise/Enterprise.cpp +++ b/Machines/Enterprise/Enterprise.cpp @@ -826,14 +826,22 @@ private: } } - void hostfs_user_write(const uint16_t address, const uint8_t value) override { - // "User" writes go to to wherever the user last had paged; + uint8_t &user_ram(const uint16_t address) { + // "User" accesses go to to wherever the user last had paged; // per 5.4 System Segment Usage those pages are stored in memory from // 0xbffc, so grab from there. const auto page_id = address >> 14; const uint8_t page = read_pointers_[0xbffc >> 14] ? read_pointers_[0xbffc >> 14][0xbffc + page_id] : 0xff; const auto offset = address & 0x3fff; - ram_segment(page)[offset] = value; + return ram_segment(page)[offset]; + } + + uint8_t hostfs_user_read(const uint16_t address) override { + return user_ram(address); + } + + void hostfs_user_write(const uint16_t address, const uint8_t value) override { + user_ram(address) = value; } void find_host_fs_hooks() { diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp index 3afb9dac5..4a493cda7 100644 --- a/Machines/Enterprise/HostFSHandler.cpp +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -40,6 +40,9 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui const auto set_b = [&](const uint8_t ch) { bc = uint16_t((bc & 0xffff) | (ch << 8)); }; + const auto b = [&]() -> uint8_t { + return bc >> 8; + }; const auto write_de = [&](const uint8_t ch) { return accessor_.hostfs_user_write(de++, ch); }; @@ -55,6 +58,11 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui set_error(EXOS::Error::NoError); return; + case uint8_t(EXOS::DeviceDescriptorFunction::Interrupt): + case uint8_t(EXOS::DeviceDescriptorFunction::BufferMoved): + set_error(EXOS::Error::NoError); + return; + // Page 54. // Emprically: C contains the unit number. case uint8_t(EXOS::Function::OpenChannel): { @@ -95,6 +103,11 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui set_error(EXOS::Error::ProtectionViolation); } } return; + + case uint8_t(EXOS::Function::SpecialFunction): + // Not supported; + set_error(EXOS::Error::InvalidSpecialFunctionCode); + return; } // @@ -110,6 +123,7 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui set_error(EXOS::Error::ChannelIllegalOrDoesNotExist); return; } + auto &file = channel->second; switch(function) { default: @@ -124,8 +138,8 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui // Page 55. case uint8_t(EXOS::Function::ReadCharacter): { - const auto next = channel->second.get(); - if(channel->second.eof()) { + const auto next = file.get(); + if(file.eof()) { set_error(EXOS::Error::EndOfFileMetInRead); } else { set_b(next); @@ -133,9 +147,17 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui } } break; + // Page 55. + case uint8_t(EXOS::Function::WriteCharacter): { + if(file.put(b())) { + set_error(EXOS::Error::NoError); + } else { + set_error(EXOS::Error::EndOfFileMetInRead); + } + } break; + // Page 55. case uint8_t(EXOS::Function::ReadBlock): { - auto &file = channel->second; set_error(EXOS::Error::NoError); while(bc) { const auto next = file.get(); @@ -148,6 +170,31 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui --bc; } } break; + + // Page 56. + case uint8_t(EXOS::Function::WriteBlock): { + set_error(EXOS::Error::NoError); + while(bc) { + const auto next = accessor_.hostfs_user_read(de); + if(!file.put(next)) { + set_error(EXOS::Error::EndOfFileMetInRead); + break; + } + + ++de; + --bc; + } + } break; + + // Page 56. + case uint8_t(EXOS::Function::ReadChannelStatus): + a = file.eof() ? 0xff : 0x00; + break; + + // Page 56. + case uint8_t(EXOS::Function::SetChannelStatus): + // TODO. + break; } } diff --git a/Machines/Enterprise/HostFSHandler.hpp b/Machines/Enterprise/HostFSHandler.hpp index 40901844f..639f33d25 100644 --- a/Machines/Enterprise/HostFSHandler.hpp +++ b/Machines/Enterprise/HostFSHandler.hpp @@ -19,10 +19,13 @@ namespace Enterprise { struct HostFSHandler { struct MemoryAccessor { + // Accessors that read from however the Z80's 64kb is currently laid out. virtual uint8_t hostfs_read(uint16_t) = 0; // virtual void hostfs_write(uint16_t, uint8_t) = 0; -// virtual uint8_t hostfs_user_read(uint16_t) = 0; + // Accessors that read from 'user' address space, i.e. the 64kb Z80 address space as currently + // mapped according to the user's preference. + virtual uint8_t hostfs_user_read(uint16_t) = 0; virtual void hostfs_user_write(uint16_t, uint8_t) = 0; }; diff --git a/Storage/FileHolder.cpp b/Storage/FileHolder.cpp index 70c16b719..7d64b8007 100644 --- a/Storage/FileHolder.cpp +++ b/Storage/FileHolder.cpp @@ -51,8 +51,8 @@ uint8_t FileHolder::get() { return uint8_t(std::fgetc(file_)); } -void FileHolder::put(const uint8_t value) { - std::fputc(value, file_); +bool FileHolder::put(const uint8_t value) { + return std::fputc(value, file_) == value; } void FileHolder::putn(std::size_t repeats, const uint8_t value) { diff --git a/Storage/FileHolder.hpp b/Storage/FileHolder.hpp index 0accb260c..21b9057c1 100644 --- a/Storage/FileHolder.hpp +++ b/Storage/FileHolder.hpp @@ -117,8 +117,11 @@ public: /*! Reads a single byte from @c file. */ uint8_t get(); - /*! Writes a single byte from @c file. */ - void put(uint8_t); + /*! + Writes a single byte from @c file. + @returns @c true on success; @c false on failure. + */ + bool put(uint8_t); /*! Writes @c value a total of @c repeats times. */ void putn(std::size_t repeats, uint8_t value); From 31d763976115955a702fef08b536ce6b63d662d9 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 21 Nov 2025 22:53:58 -0500 Subject: [PATCH 44/48] Force user/native space selection into call sites. --- Machines/Enterprise/HostFSHandler.cpp | 29 +++++++++------------------ 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp index 4a493cda7..c1be64362 100644 --- a/Machines/Enterprise/HostFSHandler.cpp +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -17,15 +17,18 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui const auto set_error = [&](const EXOS::Error error) { a = uint8_t(error); }; - const auto read_de = [&]() { - return accessor_.hostfs_read(de++); + const auto set_b = [&](const uint8_t ch) { + bc = uint16_t((bc & 0xffff) | (ch << 8)); + }; + const auto b = [&]() -> uint8_t { + return bc >> 8; }; const auto read_name = [&]() { // Get name. - uint8_t length = read_de(); + uint8_t length = accessor_.hostfs_read(de++); std::string name; while(length--) { - name.push_back(char(read_de())); + name.push_back(char(accessor_.hostfs_read(de++))); } // Use the key file if no name is specified. @@ -37,15 +40,6 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui return name; }; - const auto set_b = [&](const uint8_t ch) { - bc = uint16_t((bc & 0xffff) | (ch << 8)); - }; - const auto b = [&]() -> uint8_t { - return bc >> 8; - }; - const auto write_de = [&](const uint8_t ch) { - return accessor_.hostfs_user_write(de++, ch); - }; // // Functions that don't require an existing channel. @@ -113,12 +107,7 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui // // Functions from here require a channel already open. // - const auto channel = [&]() { - if(a == 255) { - return channels_.end(); - } - return channels_.find(a); - } (); + const auto channel = channels_.find(a); if(channel == channels_.end()) { set_error(EXOS::Error::ChannelIllegalOrDoesNotExist); return; @@ -166,7 +155,7 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui break; } - write_de(next); + accessor_.hostfs_user_write(de++, next); --bc; } } break; From 507b81a8a40447210e18434ae542cfc2455f236f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 22 Nov 2025 09:16:53 -0500 Subject: [PATCH 45/48] Implement `SetChannelStatus`. --- Machines/Enterprise/HostFSHandler.cpp | 44 +++++++++++++++++++++++++-- Storage/FileHolder.cpp | 6 ++-- Storage/FileHolder.hpp | 2 +- 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp index c1be64362..5e3b40dfe 100644 --- a/Machines/Enterprise/HostFSHandler.cpp +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -20,6 +20,9 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui const auto set_b = [&](const uint8_t ch) { bc = uint16_t((bc & 0xffff) | (ch << 8)); }; + const auto set_c = [&](const uint8_t ch) { + bc = (bc & 0xff00) | (ch); + }; const auto b = [&]() -> uint8_t { return bc >> 8; }; @@ -181,9 +184,44 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui break; // Page 56. - case uint8_t(EXOS::Function::SetChannelStatus): - // TODO. - break; + case uint8_t(EXOS::Function::SetChannelStatus): { + if(bc & 4) { + // Protection byte is not supported. + set_error(EXOS::Error::FunctionNotSupported); + break; + } + + if(bc & 1) { // User is requesting a seek. + auto pointer = de; + uint32_t file_pointer; + file_pointer = accessor_.hostfs_user_read(pointer++); + file_pointer |= uint32_t(accessor_.hostfs_user_read(pointer++) << 8); + file_pointer |= uint32_t(accessor_.hostfs_user_read(pointer++) << 16); + file_pointer |= uint32_t(accessor_.hostfs_user_read(pointer++) << 24); + + if(!file.seek(file_pointer, Storage::Whence::SET)) { + set_error(EXOS::Error::EndOfFileMetInRead); + break; + } + } + + // Fill in both position and length. + set_c(3); + const uint32_t file_pointer = uint32_t(file.tell()); + const uint32_t file_length = uint32_t(file.stats().st_size); + + auto pointer = de; + const auto write = [&](const uint32_t source) { + accessor_.hostfs_user_write(pointer++, uint8_t(source >> 0)); + accessor_.hostfs_user_write(pointer++, uint8_t(source >> 8)); + accessor_.hostfs_user_write(pointer++, uint8_t(source >> 16)); + accessor_.hostfs_user_write(pointer++, uint8_t(source >> 24)); + }; + write(file_pointer); + write(file_length); + + set_error(EXOS::Error::NoError); + } break; } } diff --git a/Storage/FileHolder.cpp b/Storage/FileHolder.cpp index 7d64b8007..8c3f73763 100644 --- a/Storage/FileHolder.cpp +++ b/Storage/FileHolder.cpp @@ -77,9 +77,9 @@ std::size_t FileHolder::write(const uint8_t *buffer, const std::size_t size) { return std::fwrite(buffer, 1, size, file_); } -void FileHolder::seek(const long offset, const Whence whence) { - [[maybe_unused]] const auto result = std::fseek(file_, offset, int(whence)); - assert(!result); +bool FileHolder::seek(const long offset, const Whence whence) { + const auto result = std::fseek(file_, offset, int(whence)); + return !result; } long FileHolder::tell() const { diff --git a/Storage/FileHolder.hpp b/Storage/FileHolder.hpp index 21b9057c1..42da2120b 100644 --- a/Storage/FileHolder.hpp +++ b/Storage/FileHolder.hpp @@ -144,7 +144,7 @@ public: std::size_t write(const uint8_t *, std::size_t); /*! Moves @c bytes from the anchor indicated by @c whence: SEEK_SET, SEEK_CUR or SEEK_END. */ - void seek(long offset, Whence); + bool seek(long offset, Whence); /*! @returns The current cursor position within this file. */ long tell() const; From e2ef3226afe2e06e39bcc96e93340c538943606a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 22 Nov 2025 09:39:05 -0500 Subject: [PATCH 46/48] Implement destroy channel. --- Machines/Enterprise/HostFSHandler.cpp | 11 +++++++++++ Storage/FileBundle/FileBundle.cpp | 6 ++++++ Storage/FileBundle/FileBundle.hpp | 2 ++ 3 files changed, 19 insertions(+) diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp index 5e3b40dfe..2006a8677 100644 --- a/Machines/Enterprise/HostFSHandler.cpp +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -128,6 +128,17 @@ void HostFSHandler::perform(const uint8_t function, uint8_t &a, uint16_t &bc, ui channels_.erase(channel); break; + // Page 54. + case uint8_t(EXOS::Function::DestroyChannel): { + const auto name = file.name(); + channels_.erase(channel); + if(bundle_->erase(name)) { + set_error(EXOS::Error::NoError); + } else { + set_error(EXOS::Error::ProtectionViolation); + } + } break; + // Page 55. case uint8_t(EXOS::Function::ReadCharacter): { const auto next = file.get(); diff --git a/Storage/FileBundle/FileBundle.cpp b/Storage/FileBundle/FileBundle.cpp index 9ce9cae79..de86925be 100644 --- a/Storage/FileBundle/FileBundle.cpp +++ b/Storage/FileBundle/FileBundle.cpp @@ -8,6 +8,8 @@ #include "FileBundle.hpp" +#include + using namespace Storage::FileBundle; LocalFSFileBundle::LocalFSFileBundle(const std::string &to_contain) { @@ -27,3 +29,7 @@ std::optional LocalFSFileBundle::key_file() { Storage::FileHolder LocalFSFileBundle::open(const std::string &name, const Storage::FileMode mode) { return Storage::FileHolder(base_path_ + name, mode); } + +bool LocalFSFileBundle::erase(const std::string &name) { + return !remove((base_path_ + name).c_str()); +} diff --git a/Storage/FileBundle/FileBundle.hpp b/Storage/FileBundle/FileBundle.hpp index 87fe04c71..cfd94d0b6 100644 --- a/Storage/FileBundle/FileBundle.hpp +++ b/Storage/FileBundle/FileBundle.hpp @@ -25,6 +25,7 @@ namespace Storage::FileBundle { struct FileBundle { virtual std::optional key_file() = 0; virtual FileHolder open(const std::string &, FileMode) = 0; + virtual bool erase(const std::string &) = 0; }; @@ -33,6 +34,7 @@ struct LocalFSFileBundle: public FileBundle { std::optional key_file() override; FileHolder open(const std::string &, FileMode) override; + bool erase(const std::string &) override; private: std::string key_file_; From 88c124aa9fa2fe5c569e5b25de7f8cd7573de833 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 22 Nov 2025 09:46:02 -0500 Subject: [PATCH 47/48] Implement destroy channel, treat it as the fallback for create channel. --- Machines/Enterprise/HostFS/hostfs.z80s | 63 ++++++++++++++++---------- Machines/Enterprise/HostFSHandler.cpp | 24 +++++----- 2 files changed, 52 insertions(+), 35 deletions(-) diff --git a/Machines/Enterprise/HostFS/hostfs.z80s b/Machines/Enterprise/HostFS/hostfs.z80s index 7e961c352..165551b73 100644 --- a/Machines/Enterprise/HostFS/hostfs.z80s +++ b/Machines/Enterprise/HostFS/hostfs.z80s @@ -18,6 +18,11 @@ db \0 endm + exoscall: macro + rst 0x30 + db \0 + endm + org 0xc000 dm "EXOS_ROM" ; Standard ROM signature. @@ -70,28 +75,10 @@ call0: call1: ld b, a ; Backup the channel number hostfscall 1 + call allocate_exos_buffer + ret z ; Exit on success. -allocate_exos_buffer: - ; Exit immediately if that call already failed. - and a - ret nz - - ; Restore the channel number and otherwise configure to allocate a buffer. - push bc - ld a, b - ld bc, 0 - ld de, 1 - - ; EXOS call 27. - rst 0x30 - db 27 - - ; If there's no error from that, exit. - pop bc - and a - ret z - - ; Otherwise, close the file again, then restore whatever the error was. What fun! + ; Otherwise, close the file and return the EXOS error. ld c, a ld a, b hostfscall 3 @@ -104,7 +91,15 @@ allocate_exos_buffer: call2: ld b, a hostfscall 2 - jp allocate_exos_buffer + call allocate_exos_buffer + ret z ; Exit on success. + + ; Otherwise, erase the newly-created file and return the EXOS error. + ld c, a + ld a, b + hostfscall 4 + ld a, c + ret ; ; Close channel. @@ -181,8 +176,7 @@ call12: ; ld de, device_name ld c, 1 - rst 0x30 - db 19 + exoscall 19 ret ; @@ -191,3 +185,24 @@ call12: call13: hostfscall 13 ret + +; +; Attempts to allocate EXOS storage for a channel. +; Returns Z set for success, clear for failure. +; +allocate_exos_buffer: + ; Exit immediately if that call already failed. + and a + ret nz + + ; Restore the channel number and otherwise configure to allocate a buffer. + push bc + ld a, b + ld bc, 0 + ld de, 1 + exoscall 27 + + ; If there's no error from that, exit. + pop bc + and a + ret diff --git a/Machines/Enterprise/HostFSHandler.cpp b/Machines/Enterprise/HostFSHandler.cpp index 2006a8677..25afdcdc4 100644 --- a/Machines/Enterprise/HostFSHandler.cpp +++ b/Machines/Enterprise/HostFSHandler.cpp @@ -245,16 +245,18 @@ std::vector HostFSHandler::rom() { return std::vector{ 0x45, 0x58, 0x4f, 0x53, 0x5f, 0x52, 0x4f, 0x4d, 0x1b, 0x40, 0xc9, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x1d, 0x40, 0x00, 0x00, 0x04, 0x46, - 0x49, 0x4c, 0x45, 0x0c, 0x00, 0x39, 0xc0, 0x3a, 0xc0, 0x56, 0xc0, 0x5e, - 0xc0, 0x63, 0xc0, 0x68, 0xc0, 0x6d, 0xc0, 0x72, 0xc0, 0x77, 0xc0, 0x7c, - 0xc0, 0x81, 0xc0, 0x86, 0xc0, 0x8b, 0xc0, 0x97, 0xc0, 0xc9, 0x47, 0xed, - 0xfe, 0xfe, 0x01, 0xa7, 0xc0, 0xc5, 0x78, 0x01, 0x00, 0x00, 0x11, 0x01, - 0x00, 0xf7, 0x1b, 0xc1, 0xa7, 0xc8, 0x4f, 0x78, 0xed, 0xfe, 0xfe, 0x03, - 0x79, 0xc9, 0x47, 0xed, 0xfe, 0xfe, 0x02, 0xc3, 0x3f, 0xc0, 0xed, 0xfe, - 0xfe, 0x03, 0xc9, 0xed, 0xfe, 0xfe, 0x04, 0xc9, 0xed, 0xfe, 0xfe, 0x05, - 0xc9, 0xed, 0xfe, 0xfe, 0x06, 0xc9, 0xed, 0xfe, 0xfe, 0x07, 0xc9, 0xed, - 0xfe, 0xfe, 0x08, 0xc9, 0xed, 0xfe, 0xfe, 0x09, 0xc9, 0xed, 0xfe, 0xfe, - 0x0a, 0xc9, 0xed, 0xfe, 0xfe, 0x0b, 0xc9, 0xed, 0xfe, 0xfe, 0x0c, 0x11, - 0x16, 0xc0, 0x0e, 0x01, 0xf7, 0x13, 0xc9, 0xed, 0xfe, 0xfe, 0x0d, 0xc9 + 0x49, 0x4c, 0x45, 0x0c, 0x00, 0x39, 0xc0, 0x3a, 0xc0, 0x4b, 0xc0, 0x5c, + 0xc0, 0x61, 0xc0, 0x66, 0xc0, 0x6b, 0xc0, 0x70, 0xc0, 0x75, 0xc0, 0x7a, + 0xc0, 0x7f, 0xc0, 0x84, 0xc0, 0x89, 0xc0, 0x95, 0xc0, 0xc9, 0x47, 0xed, + 0xfe, 0xfe, 0x01, 0xcd, 0x9a, 0xc0, 0xc8, 0x4f, 0x78, 0xed, 0xfe, 0xfe, + 0x03, 0x79, 0xc9, 0x47, 0xed, 0xfe, 0xfe, 0x02, 0xcd, 0x9a, 0xc0, 0xc8, + 0x4f, 0x78, 0xed, 0xfe, 0xfe, 0x04, 0x79, 0xc9, 0xed, 0xfe, 0xfe, 0x03, + 0xc9, 0xed, 0xfe, 0xfe, 0x04, 0xc9, 0xed, 0xfe, 0xfe, 0x05, 0xc9, 0xed, + 0xfe, 0xfe, 0x06, 0xc9, 0xed, 0xfe, 0xfe, 0x07, 0xc9, 0xed, 0xfe, 0xfe, + 0x08, 0xc9, 0xed, 0xfe, 0xfe, 0x09, 0xc9, 0xed, 0xfe, 0xfe, 0x0a, 0xc9, + 0xed, 0xfe, 0xfe, 0x0b, 0xc9, 0xed, 0xfe, 0xfe, 0x0c, 0x11, 0x16, 0xc0, + 0x0e, 0x01, 0xf7, 0x13, 0xc9, 0xed, 0xfe, 0xfe, 0x0d, 0xc9, 0xa7, 0xc0, + 0xc5, 0x78, 0x01, 0x00, 0x00, 0x11, 0x01, 0x00, 0xf7, 0x1b, 0xc1, 0xa7, + 0xc9 }; } From 2dd2b279c8293961e6a9c96360951bf3333ac40c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 22 Nov 2025 09:49:06 -0500 Subject: [PATCH 48/48] Note reason for that particular way of marking code. --- Machines/Enterprise/HostFS/hostfs.z80s | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Machines/Enterprise/HostFS/hostfs.z80s b/Machines/Enterprise/HostFS/hostfs.z80s index 165551b73..90ed770dd 100644 --- a/Machines/Enterprise/HostFS/hostfs.z80s +++ b/Machines/Enterprise/HostFS/hostfs.z80s @@ -13,6 +13,13 @@ ; on the device chain ; + + ; + ; This code adapts the same mechanism for a host call as that used by EP128Emu's FILE IO ROM. + ; My original thinking was that one could be substituted for the other to permit comparative testing. + ; EP128 has a couple of emulator-specific call codes that I don't implement though, and otherwise + ; doesn't seem to work in this emulator. And likely the converse holds. + ; hostfscall: macro db 0xed, 0xfe, 0xfe db \0