Add a through path for Archimedes disk images.

This commit is contained in:
Thomas Harte 2024-03-04 10:13:57 -05:00
parent 95cc34ba23
commit eae92a0cdb
4 changed files with 66 additions and 43 deletions

View File

@ -17,6 +17,7 @@ enum class Machine {
Atari2600,
AtariST,
Amiga,
Archimedes,
ColecoVision,
Electron,
Enterprise,

View File

@ -87,37 +87,45 @@ std::unique_ptr<Catalogue> Analyser::Static::Acorn::GetADFSCatalogue(const std::
auto catalogue = std::make_unique<Catalogue>();
Storage::Encodings::MFM::Parser parser(Storage::Encodings::MFM::Density::Double, disk);
const Storage::Encodings::MFM::Sector *free_space_map_second_half = parser.sector(0, 0, 1);
// Grab the second half of the free-space map because it has the boot option in it.
const Storage::Encodings::MFM::Sector *const free_space_map_second_half = parser.sector(0, 0, 1);
if(!free_space_map_second_half) return nullptr;
const bool has_large_sectors = free_space_map_second_half->samples[0].size() == 1024;
std::vector<uint8_t> root_directory;
root_directory.reserve(5 * 256);
for(uint8_t c = 2; c < 7; c++) {
root_directory.reserve((has_large_sectors ? 5 : 8) * 256);
for(uint8_t c = 2; c < (has_large_sectors ? 4 : 7); c++) {
const Storage::Encodings::MFM::Sector *const sector = parser.sector(0, 0, c);
if(!sector) return nullptr;
root_directory.insert(root_directory.end(), sector->samples[0].begin(), sector->samples[0].end());
}
// Quick sanity checks.
if(root_directory[0x4cb]) return nullptr;
catalogue->is_hugo = !memcmp(&root_directory[1], "Hugo", 4) && !memcmp(&root_directory[0x4FB], "Hugo", 4);
const bool is_nick = !memcmp(&root_directory[1], "Nick", 4) && !memcmp(&root_directory[0x4FB], "Nick", 4);
// Check for end of directory marker.
if(root_directory[has_large_sectors ? 0x7d7 : 0x4cb]) return nullptr;
// Check for both directory identifiers.
catalogue->is_hugo = !memcmp(&root_directory[1], "Hugo", 4) && !memcmp(&root_directory[0x4fb], "Hugo", 4);
const bool is_nick = !memcmp(&root_directory[1], "Nick", 4) && !memcmp(&root_directory[0x7fb], "Nick", 4);
if(!catalogue->is_hugo && !is_nick) {
return nullptr;
}
switch(free_space_map_second_half->samples[0][0xfd]) {
default: catalogue->bootOption = Catalogue::BootOption::None; break;
case 1: catalogue->bootOption = Catalogue::BootOption::LoadBOOT; break;
case 2: catalogue->bootOption = Catalogue::BootOption::RunBOOT; break;
case 3: catalogue->bootOption = Catalogue::BootOption::ExecBOOT; break;
if(!has_large_sectors) {
// TODO: I don't know where the boot option rests with large sectors.
switch(free_space_map_second_half->samples[0][0xfd]) {
default: catalogue->bootOption = Catalogue::BootOption::None; break;
case 1: catalogue->bootOption = Catalogue::BootOption::LoadBOOT; break;
case 2: catalogue->bootOption = Catalogue::BootOption::RunBOOT; break;
case 3: catalogue->bootOption = Catalogue::BootOption::ExecBOOT; break;
}
}
// Parse the root directory, at least.
for(std::size_t file_offset = 0x005; file_offset < 0x4cb; file_offset += 0x1a) {
for(std::size_t file_offset = 0x005; file_offset < (has_large_sectors ? 0x7d7 : 0x4cb); file_offset += 0x1a) {
// Obtain the name, which will be at most ten characters long, and will
// be terminated by either a NULL character or a \r.
char name[11];
char name[11]{};
std::size_t c = 0;
for(; c < 10; c++) {
const char next = root_directory[file_offset + c] & 0x7f;

View File

@ -60,10 +60,11 @@ static std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>>
}
Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) {
auto target = std::make_unique<Target>();
auto target8bit = std::make_unique<Target>();
auto targetArchimedes = std::make_unique<Analyser::Static::Target>(Machine::Archimedes);
// Strip out inappropriate cartridges.
target->media.cartridges = AcornCartridgesFrom(media.cartridges);
// Copy appropriate cartridges to the 8-bit target.
target8bit->media.cartridges = AcornCartridgesFrom(media.cartridges);
// If there are any tapes, attempt to get data from the first.
if(!media.tapes.empty()) {
@ -94,9 +95,9 @@ Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &me
// Inspect first file. If it's protected or doesn't look like BASIC
// then the loading command is *RUN. Otherwise it's CHAIN"".
target->loading_command = is_basic ? "CHAIN\"\"\n" : "*RUN\n";
target8bit->loading_command = is_basic ? "CHAIN\"\"\n" : "*RUN\n";
target->media.tapes = media.tapes;
target8bit->media.tapes = media.tapes;
}
}
@ -112,16 +113,16 @@ Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &me
if(dfs_catalogue || (adfs_catalogue && adfs_catalogue->is_hugo)) {
// Accept the disk and determine whether DFS or ADFS ROMs are implied.
// Use the Pres ADFS if using an ADFS, as it leaves Page at &EOO.
target->media.disks = media.disks;
target->has_dfs = bool(dfs_catalogue);
target->has_pres_adfs = bool(adfs_catalogue);
target8bit->media.disks = media.disks;
target8bit->has_dfs = bool(dfs_catalogue);
target8bit->has_pres_adfs = bool(adfs_catalogue);
// Check whether a simple shift+break will do for loading this disk.
Catalogue::BootOption bootOption = (dfs_catalogue ?: adfs_catalogue)->bootOption;
if(bootOption != Catalogue::BootOption::None) {
target->should_shift_restart = true;
target8bit->should_shift_restart = true;
} else {
target->loading_command = "*CAT\n";
target8bit->loading_command = "*CAT\n";
}
// Check whether adding the AP6 ROM is justified.
@ -137,39 +138,44 @@ Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &me
"VERIFY", "ZERO"
}) {
if(std::search(file.data.begin(), file.data.end(), command, command+strlen(command)) != file.data.end()) {
target->has_ap6_rom = true;
target->has_sideways_ram = true;
target8bit->has_ap6_rom = true;
target8bit->has_sideways_ram = true;
}
}
}
} else if(adfs_catalogue) {
targetArchimedes->media.disks = media.disks;
}
}
// Enable the Acorn ADFS if a mass-storage device is attached;
// unlike the Pres ADFS it retains SCSI logic.
if(!media.mass_storage_devices.empty()) {
target->has_pres_adfs = false; // To override a floppy selection, if one was made.
target->has_acorn_adfs = true;
target8bit->has_pres_adfs = false; // To override a floppy selection, if one was made.
target8bit->has_acorn_adfs = true;
// Assume some sort of later-era Acorn work is likely to happen;
// so ensure *TYPE, etc are present.
target->has_ap6_rom = true;
target->has_sideways_ram = true;
target8bit->has_ap6_rom = true;
target8bit->has_sideways_ram = true;
target->media.mass_storage_devices = media.mass_storage_devices;
target8bit->media.mass_storage_devices = media.mass_storage_devices;
// Check for a boot option.
const auto sector = target->media.mass_storage_devices.front()->get_block(1);
const auto sector = target8bit->media.mass_storage_devices.front()->get_block(1);
if(sector[0xfd]) {
target->should_shift_restart = true;
target8bit->should_shift_restart = true;
} else {
target->loading_command = "*CAT\n";
target8bit->loading_command = "*CAT\n";
}
}
TargetList targets;
if(!target->media.empty()) {
targets.push_back(std::move(target));
if(!target8bit->media.empty()) {
targets.push_back(std::move(target8bit));
}
if(!targetArchimedes->media.empty()) {
targets.push_back(std::move(targetArchimedes));
}
return targets;
}

View File

@ -144,13 +144,21 @@ struct ActivityObserver: public Activity::Observer {
ROM::Request missing_roms;
_machine = Machine::MachineForTargets(_analyser.targets, CSROMFetcher(&missing_roms), error);
if(!_machine) {
const std::wstring description = missing_roms.description(0, L'•');
static_assert(sizeof(wchar_t) == 4, "This code assumes wchar_t is UTF32");
NSString *nativeString = [[NSString alloc]
initWithBytes:description.data()
length:description.size()*sizeof(wchar_t)
encoding:NSUTF32LittleEndianStringEncoding];
[missingROMs appendString:nativeString];
switch(error) {
case Machine::Error::MissingROM: {
const std::wstring description = missing_roms.description(0, L'•');
static_assert(sizeof(wchar_t) == 4, "This code assumes wchar_t is UTF32");
NSString *nativeString = [[NSString alloc]
initWithBytes:description.data()
length:description.size()*sizeof(wchar_t)
encoding:NSUTF32LittleEndianStringEncoding];
[missingROMs appendString:nativeString];
} break;
default:
NSLog(@"Unhandled machine creation error %d", error);
break;
}
return nil;
}
updater.performer.machine = _machine.get();