1
0
mirror of https://github.com/TomHarte/CLK.git synced 2026-03-11 19:41:57 +00:00
Files
CLK/Machines/Enterprise/HostFSHandler.cpp
2025-11-26 15:35:28 -05:00

264 lines
7.2 KiB
C++

//
// 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(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 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;
};
const auto read_name = [&]() {
// Get name.
uint8_t length = accessor_.hostfs_read(de++);
std::string name;
while(length--) {
name.push_back(char(accessor_.hostfs_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;
};
//
// 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;
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): {
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;
case uint8_t(EXOS::Function::SpecialFunction):
// Not supported;
set_error(EXOS::Error::InvalidSpecialFunctionCode);
return;
}
//
// Functions from here require a channel already open.
//
const auto channel = channels_.find(a);
if(channel == channels_.end()) {
set_error(EXOS::Error::ChannelIllegalOrDoesNotExist);
return;
}
auto &file = channel->second;
switch(function) {
default:
printf("UNIMPLEMENTED function %d with A:%02x BC:%04x DE:%04x\n", function, a, bc, de);
break;
// Page 54.
case uint8_t(EXOS::Function::CloseChannel):
set_error(EXOS::Error::NoError);
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();
if(file.eof()) {
set_error(EXOS::Error::EndOfFileMetInRead);
} else {
set_b(next);
set_error(EXOS::Error::NoError);
}
} 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): {
set_error(EXOS::Error::NoError);
while(bc) {
const auto next = file.get();
if(channel->second.eof()) {
set_error(EXOS::Error::EndOfFileMetInRead);
break;
}
accessor_.hostfs_user_write(de++, next);
--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): {
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;
}
}
void HostFSHandler::set_file_bundle(std::shared_ptr<Storage::FileBundle::FileBundle> bundle) {
bundle_ = bundle;
bundle_->set_case_insensitive(true);
}
std::vector<uint8_t> HostFSHandler::rom() {
// Assembled and transcribed from hostfs.z80.
return std::vector<uint8_t>{
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, 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
};
}